假设我有下面的类X,我想返回一个内部成员的访问:
class Z
{
// details
};
class X
{
std::vector<Z> vecZ;
public:
Z& Z(size_t index)
{
// massive amounts of code for validating index
Z& ret = vecZ[index];
// even more code for determining that the Z instance
// at index is *exactly* the right sort of Z (a process
// which involves calculating leap years in which
// religious holidays fall on Tuesdays for
// the next thousand years or so)
return ret;
}
const Z& Z(size_t index) const
{
// identical to non-const X::Z(), except printed in
// a lighter shade of gray since
// we're running low on toner by this point
}
};
两个成员函数X::Z()和X::Z() const在大括号内具有相同的代码。这是重复的代码,可能会导致具有复杂逻辑的长函数的维护问题。
有办法避免这种代码重复吗?
c++ 17更新了这个问题的最佳答案:
T const & f() const {
return something_complicated();
}
T & f() {
return const_cast<T &>(std::as_const(*this).f());
}
这样做的好处是:
很明显发生了什么
有最小的代码开销——它适合单行
很难出错(只能抛弃不稳定的偶然,但不稳定是一个罕见的限定词)
如果你想要走完整的演绎路线,那么可以通过一个辅助函数来完成
template<typename T>
constexpr T & as_mutable(T const & value) noexcept {
return const_cast<T &>(value);
}
template<typename T>
constexpr T * as_mutable(T const * value) noexcept {
return const_cast<T *>(value);
}
template<typename T>
constexpr T * as_mutable(T * value) noexcept {
return value;
}
template<typename T>
void as_mutable(T const &&) = delete;
现在你甚至不能搞混volatile,它的用法看起来就像
decltype(auto) f() const {
return something_complicated();
}
decltype(auto) f() {
return as_mutable(std::as_const(*this).f());
}
如果你不喜欢const强制转换,我使用这个c++ 17版本的模板静态帮助器函数,由另一个答案建议,并带有可选的SFINAE测试。
#include <type_traits>
#define REQUIRES(...) class = std::enable_if_t<(__VA_ARGS__)>
#define REQUIRES_CV_OF(A,B) REQUIRES( std::is_same_v< std::remove_cv_t< A >, B > )
class Foobar {
private:
int something;
template<class FOOBAR, REQUIRES_CV_OF(FOOBAR, Foobar)>
static auto& _getSomething(FOOBAR& self, int index) {
// big, non-trivial chunk of code...
return self.something;
}
public:
auto& getSomething(int index) { return _getSomething(*this, index); }
auto& getSomething(int index) const { return _getSomething(*this, index); }
};
完整版:https://godbolt.org/z/mMK4r3