假设我有下面的类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在大括号内具有相同的代码。这是重复的代码,可能会导致具有复杂逻辑的长函数的维护问题。

有办法避免这种代码重复吗?


当前回答

要添加到jwfearn和kevin提供的解决方案,下面是函数返回shared_ptr时对应的解决方案:

struct C {
  shared_ptr<const char> get() const {
    return c;
  }
  shared_ptr<char> get() {
    return const_pointer_cast<char>(static_cast<const C &>(*this).get());
  }
  shared_ptr<char> c;
};

其他回答

有关详细说明,请参阅《Effective c++》第23页第3项“尽可能使用const”中的标题“避免const和非const成员函数中的重复”,由Scott Meyers 3d编辑,ISBN-13: 9780321334879。

以下是Meyers的解决方案(简化版):

struct C {
  const char & get() const {
    return c;
  }
  char & get() {
    return const_cast<char &>(static_cast<const C &>(*this).get());
  }
  char c;
};

这两个类型转换和函数调用可能很难看,但在非const方法中是正确的,因为这意味着对象一开始就不是const对象。(Meyers对此进行了深入的讨论。)

通常,需要使用const版本和非const版本的成员函数是getter和setter。大多数时候它们都是一行程序,所以代码复制不是问题。

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());
}

我建议使用私有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);
    }
};

这篇DDJ文章展示了一种不需要使用const_cast就可以使用模板专门化的方法。对于这样一个简单的函数,它确实是不需要的。

Boost::any_cast(在某一时刻,它不再使用)使用const版本的const_cast调用非const版本以避免重复。你不能把const语义强加在非const版本上,所以你必须非常小心。

最后,一些代码复制是可以的,只要这两个代码段直接在彼此之上。