假设我有下面的类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在大括号内具有相同的代码。这是重复的代码,可能会导致具有复杂逻辑的长函数的维护问题。
有办法避免这种代码重复吗?
我认为Scott Meyers的解决方案可以在c++ 11中通过使用tempate helper函数进行改进。这使得意图更加明显,并且可以被许多其他getter重用。
template <typename T>
struct NonConst {typedef T type;};
template <typename T>
struct NonConst<T const> {typedef T type;}; //by value
template <typename T>
struct NonConst<T const&> {typedef T& type;}; //by reference
template <typename T>
struct NonConst<T const*> {typedef T* type;}; //by pointer
template <typename T>
struct NonConst<T const&&> {typedef T&& type;}; //by rvalue-reference
template<typename TConstReturn, class TObj, typename... TArgs>
typename NonConst<TConstReturn>::type likeConstVersion(
TObj const* obj,
TConstReturn (TObj::* memFun)(TArgs...) const,
TArgs&&... args) {
return const_cast<typename NonConst<TConstReturn>::type>(
(obj->*memFun)(std::forward<TArgs>(args)...));
}
可以通过以下方式使用此helper函数。
struct T {
int arr[100];
int const& getElement(size_t i) const{
return arr[i];
}
int& getElement(size_t i) {
return likeConstVersion(this, &T::getElement, i);
}
};
第一个参数总是this指针。第二个是指向要调用的成员函数的指针。在此之后,可以传递任意数量的附加参数,以便将它们转发给函数。
这需要c++ 11,因为有可变模板。
是的,可以避免代码重复。你需要使用const成员函数来拥有逻辑,并让非const成员函数调用const成员函数,并将返回值重新转换为非const引用(或指针,如果函数返回指针):
class X
{
std::vector<Z> vecZ;
public:
const Z& z(size_t index) const
{
// same really-really-really long access
// and checking code as in OP
// ...
return vecZ[index];
}
Z& z(size_t index)
{
// One line. One ugly, ugly line - but just one line!
return const_cast<Z&>( static_cast<const X&>(*this).z(index) );
}
#if 0 // A slightly less-ugly version
Z& Z(size_t index)
{
// Two lines -- one cast. This is slightly less ugly but takes an extra line.
const X& constMe = *this;
return const_cast<Z&>( constMe.z(index) );
}
#endif
};
注意:重要的是,不要将逻辑放在非const函数中,并让const函数调用非const函数——这可能会导致未定义的行为。原因是常量类实例被转换为非常量实例。非const成员函数可能会意外地修改类,c++标准状态将导致未定义的行为。
我建议使用私有helper静态函数模板,如下所示:
class X
{
std::vector<Z> vecZ;
// ReturnType is explicitly 'Z&' or 'const Z&'
// ThisType is deduced to be 'X' or 'const X'
template <typename ReturnType, typename ThisType>
static ReturnType Z_impl(ThisType& self, size_t index)
{
// massive amounts of code for validating index
ReturnType ret = self.vecZ[index];
// even more code for determining, blah, blah...
return ret;
}
public:
Z& Z(size_t index)
{
return Z_impl<Z&>(*this, index);
}
const Z& Z(size_t index) const
{
return Z_impl<const Z&>(*this, index);
}
};