假设您接受 const 正确性作为一种技术,那么我认为这意味着您更喜欢编译器检查的 const 正确性而不是简洁。所以你希望编译器检查两件事:
- 当“this”是非常量时,调用者可以安全地修改您返回的指针的引用。
- 当“this”为 const 时,无论您做什么工作都不会对“this”执行任何非 const 操作。
如果 const 版本调用非常量,那么您不会得到 (2)。如果非常量版本调用 const 并且 const_casts 结果,那么您不会得到 (1)。例如假设Bar
is 实际上char
,并且您编写的代码最终返回(在某些情况下)字符串文字。这将编译(并且 -Wwrite-strings 不会给出警告),但是您的调用者最终会得到一个指向字符串文字的非常量指针。这与“您更喜欢编译器检查的 const 正确性”相矛盾。
如果它们都调用辅助成员函数Bar *getBar() const
,那么您将同时得到 (1) 和 (2)。但是,如果可以编写该辅助函数,那么当修改从 const Foo 返回的 Bar 完全可以时,为什么要首先搞乱 const 和非 const 版本?有时,实现的一些细节可能意味着您正在实现一个具有两个访问器的接口,即使您只需要一个访问器。否则,要么不能编写帮助器,要么只能用单个帮助器替换这两个函数。
只要代码大小不是问题,我认为实现(1)和(2)的最佳方法是让编译器实际考虑这两种情况:
struct Bar { int a; };
struct Foo {
Bar *bar() { return getBar<Bar>(this); }
const Bar *bar() const { return getBar<const Bar>(this); }
Bar *bar2() const { return getBar<Bar>(this); } // doesn't compile. Good.
Bar *bar3() const { return getBar<const Bar>(this); } // likewise
private:
template <typename B, typename F>
static B *getBar(F *self) {
// non-trivial code, which can safely call other functions with
// const/non-const overloads, and we don't have to manually figure out
// whether it's safe to const_cast the result.
return &self->myBar;
}
Bar myBar;
};
如果代码很简单,比如operator[]
访问对象拥有的某个数组,那么我只需复制代码。在某些时候,上述函数模板的编码工作量比复制要少,此时使用模板。
我认为 const_cast 方法虽然聪明且看似标准,但并没有帮助,因为它选择简洁而不是编译器检查的 const 正确性。如果方法中的代码很简单,那么您可以复制它。如果它不是微不足道的,那么您或代码维护人员就不容易看到 const_cast 实际上是有效的。