简化const重载?

我一直在教C ++编程类多年,向学生解释的一个最棘手的问题是const重载。 我通常使用向量类的例子和它的operator[]函数:

 template <typename T> class Vector { public: T& operator[] (size_t index); const T& operator[] (size_t index) const; }; 

我很难解释为什么需要两个版本的operator[]函数,但是在试图解释如何将两个实现结合在一起时,我经常发现自己浪费了大量的语言arcana。 问题是,我知道如何以另一种方式实现这些函数之一的唯一好的,可靠的方法是使用const_cast / static_cast技巧:

 template <typename T> const T& Vector<T>::operator[] (size_t index) const { /* ... your implementation here ... */ } template <typename T> T& Vector<T>::operator[] (size_t index) { return const_cast<T&>(static_cast<const Vector&>(*this)[index]); } 

这个设置的问题是解释非常棘手,而且一点也不明显。 当你把它解释为“cast to const,然后调用const版本,然后去掉const”,这有点容易理解,但实际的语法是可怕的。 解释什么是const_cast ,为什么它在这里是适当的,为什么在其他地方几乎普遍不合适,通常需要我花费五到十分钟的时间,而理解这个整个expression式通常需要比const T*T* const之间的区别更多的努力。 我觉得学生需要了解常量重载以及如何做,而不必在两个函数中不必要地复制代码,但是这个技巧在介绍性的C ++编程课程中似乎有点过分。

我的问题是这样的 – 是否有一个更简单的方法来实现相互之间的const重载函数? 还是有一种更简单的方式来向学生解释这个现有的技巧?

如何简单地把它分解成更小的步骤?

 const Vector<T>& const_this = *this; const T& const_elem = const_this[index]; T& mutable_elem = const_cast<T&>(const_elem); return mutable_elem; 

你甚至可以用这种方法消除这个static_cast ,但是如果你认为它会更清楚,你可以把它留下。

我通常认为这是一种语言限制,并build议人们 – 除非他们真的知道他们在做什么 – 他们应该只是重新实现。 在绝大多数情况下,这些function都是简单的单线获取器,所以没有任何痛苦。

在你教C ++的能力方面,我会更加强烈地感觉到这种方法。

这是一个相当奇怪的select,但这可以使用这样的静态模板帮手来完成

 // template parameters can be const or non-const template<class Ret, class C> static Ret& get(C* p, size_t index) { /* common code here like p->array[index] */ } 

然后你可以写

 const T& operator[](size_t index) const { return get<const T>(this, index); } T& operator[](size_t index) { return get<T>(this, index); } 

这个技巧避免了强制转换(!)和双重实现,但是,再一次,它对我来说看起来很奇怪:)

而关于你的snippet代码的小意见,不会const_cast而不是static_cast ,或者我错过了什么?

您可以使用私有方法删除一个强制转换:
它增加了一个方法,但是使得铸件不那么复杂:

 template <typename T> class Vector { public: T const& operator[](size_t i) const { return getValue(i);} T& operator[](size_t i) { return const_cast<T&>(getValue(i));} private: T const& getValue(size_t i) const { return /* STUFF */;} }; 

在我看来,这是愚蠢的。 你只是为了这样做而强迫其他人实现,而不是因为结果代码更容易维护或理解。 你的学生感到困惑的原因可能是因为他们应该。

不是每一个原则都应该被认为是独一无二的。 有时候多余只是更好。

而不是让一个版本调用另一个,你可以让这两个调用一个帮助函数,find正确的项目。 您似乎已经引入了模板,所以让辅助函数也是一个模板,应该工作并避免代码重复,并且在没有任何const_cast的情况下为const和non-const工作。

或者,您可以使用局部variables来帮助将expression式分解为可pipe理的块(每个块都有注释)。

 const ClassType& cthis = *this; // look, no explicit cast needed here :) const T& elem = cthis[index]; // delegate to const version return const_cast<T&>(elem); // ok to strip off the const, since we added it in the first place 

如果实现将完全相同的代码(对于这个“向量”类示例或其他),那么为什么不让非const的版本调用const版本,而不是相反。 如果由于某种原因代码必须修改一个成员,那么也许不应该有一个真正的const版本(忽略整个可变的东西…)。