无需const_cast,如何让C++数据成员构造后变为const(含昂贵中间值场景)
当存在计算成本高昂、需用于计算多个数据成员的中间值时,不依赖const_cast,如何让C++的数据成员在构造完成后而非构造期间变为const?
以下是一个最小化、完整可验证的示例,进一步解释问题及原因。建议先阅读示例中的两处注释以节省时间。
#include <iostream> namespace { constexpr int initializer {3}; constexpr int ka {10}; constexpr int kb {25}; class T { private: int value; const int a_; const int b_; public: T(int n); inline int operator()() const { return value; } inline int a() const { return a_; } inline int b() const { return b_; } int &operator--(); }; T::T(const int n): value {n - 1}, a_ {0}, b_ {0} { // The integer expensive // + is to be computed only once and, // + after the T object has been constructed, // is not to be stored. // These requirements must be met without reliance // on the compiler's optimizer. const int expensive {n*n*n - 1}; const_cast<int &>(a_) = ka*expensive; const_cast<int &>(b_) = kb*expensive; } int &T::operator--() { --value; // To alter a_ or b_ is forbidden. Therefore, the compiler // must abort compilation if the next line is uncommented. //--a_; --b_; return value; } } int main() { T t(initializer); std::cout << "before decrement, t() == " << t() << "\n"; --t; std::cout << "after decrement, t() == " << t() << "\n"; std::cout << "t.a() == " << t.a() << "\n"; std::cout << "t.b() == " << t.b() << "\n"; return 0; }
程序输出
before decrement, t() == 2 after decrement, t() == 1 t.a() == 260 t.b() == 650
(我了解此前的一个入门级问题,但该问题仅涉及基础场景。请参考上述代码中的注释。我的诉求是:避免重复执行昂贵的初始化操作,不存储中间结果,同时让构造完成后的常量数据成员受编译器保护,且不使用const_cast、不浪费资源。我并非询问如何避免常量数据成员,而是探讨该场景下的实现方案。)
后续思考
在阅读答案并试验后,我认为自己方法有误。尽管C++支持const数据成员,但它们的使用常与常规数据范式相悖——可变对象的const成员并非真正意义上的常量,可通过父对象赋值覆盖,不符合预期用途。
@Homer512的评论指出问题:
Don't overstress yourself into making members
constwhen it is inconvenient. If anything, it can lead to inefficient code generation, e.g. by making move-construction fall back to copy constructions.
防止无意修改的正确方式是不提供修改接口;若需保护成员不受类自身函数修改,@Some programmer dude的答案给出了实现方法。我现在怀疑C++中能否顺畅处理const数据成员,这种场景下const的保护对象并不恰当。
内容的提问来源于stack exchange,提问作者thb

