假设我有以下class 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
在大括号内具有相同的代码。这是重复的代码,可能会导致具有复杂逻辑的长函数的维护问题。
有没有办法避免这种代码重复?
发布于 2008-09-23 20:48:04
是的,可以避免代码重复。您需要使用常量成员函数来获得逻辑,并让非常数成员函数调用常量成员函数,并将返回值重新转换为非常数引用(如果函数返回指针,则为指针):
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
};
注意:重要的是,不要将逻辑放在非常数函数中,并让常量函数调用非常数函数--这可能会导致未定义的行为。原因是常量类实例被转换为非常量实例。非常数成员函数可能会意外地修改类,这将导致C++标准规定的未定义行为。
发布于 2008-09-23 21:24:14
有关详细解释,请参阅Scott Meyer在, 3d ed中的第3项“尽可能使用const
”中的标题“避免在const
和非const
成员函数中复制”,ISBN-13: 9780321334879。
以下是Meyer的解决方案(简化版):
struct C {
const char & get() const {
return c;
}
char & get() {
return const_cast<char &>(static_cast<const C &>(*this).get());
}
char c;
};
两种类型转换和函数调用可能很难看,但在非const
方法中是正确的,因为这意味着对象一开始就不是const
。(迈耶斯对此进行了彻底的讨论。)
发布于 2017-11-19 01:51:59
C++17已经更新了这个问题的最佳答案:
T const & f() const {
return something_complicated();
}
T & f() {
return const_cast<T &>(std::as_const(*this).f());
}
这样做的好处是:
volatile
,但volatile
是一个罕见的限定符)< code >F210
如果你想走完整的演绎路线,那么可以通过一个helper函数来实现
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());
}
https://stackoverflow.com/questions/123758
复制相似问题