如何让C++静态成员引用不可变而非引用不可变对象?
首先得纠正一个认知偏差:C++里的引用一旦初始化就绝对不能被重新绑定到其他对象,你担心的BundleOfInterfaces::interface1 = MyNewInterface1;根本不是在改引用的指向——这行代码实际是在调用Interface1的赋值运算符,把MyNewInterface1的内容塞给了interface1引用的那个对象。
你想要的是“禁止通过这个引用给对象赋值,但允许调用对象的非const方法”,直接用const修饰引用本身是做不到的:因为const加在引用上时,作用的是引用指向的对象(比如const Interface1&会让对象变成const,所有非const方法都没法调用),而引用本身的不可绑定是语法自带的,不需要额外加const。
不过可以用和const相关的技巧间接实现需求,下面是两种可行方案:
方案1:用不可赋值的包装类
写一个简单的模板包装类,持有非const接口引用,转发所有成员方法,但禁用赋值操作:
template<typename T> class NonAssignableRef { public: explicit NonAssignableRef(T& ref) : ref_(ref) {} // 让包装类能像原引用一样调用成员方法 T* operator->() { return &ref_; } T& operator*() { return ref_; } // 禁止任何赋值操作 NonAssignableRef& operator=(const NonAssignableRef&) = delete; NonAssignableRef& operator=(const T&) = delete; private: T& ref_; }; // 修改你的接口束结构 struct BundleOfInterfaces { static NonAssignableRef<Interface1> interface1; static NonAssignableRef<Interface2> interface2; // ... }; // 初始化时绑定到实际的接口实例 Interface1 actualInterface1; NonAssignableRef<Interface1> BundleOfInterfaces::interface1(actualInterface1);
这样调用BundleOfInterfaces::interface1->function1(...)完全正常,但写BundleOfInterfaces::interface1 = MyNewInterface1;会直接编译报错,因为赋值运算符被删掉了。
方案2:私有const引用加getter(变种)
这本质上是你提到的私有引用方案,但借助了const来限制直接访问:
struct BundleOfInterfaces { // 对外返回非const引用,允许调用非const方法 static Interface1& interface1() { return const_cast<Interface1&>(interface1_); } private: // 私有const引用,外部无法直接修改或赋值 static const Interface1& interface1_; }; // 初始化时绑定到非const的实际实例 Interface1 actualInterface1; const Interface1& BundleOfInterfaces::interface1_ = actualInterface1;
注意这种方式里const_cast是安全的,因为我们绑定的本来就是非const对象。外部只能通过interface1()获取引用,虽然还是能写interface1() = ...,但这和直接操作对象赋值是一回事——如果要彻底禁止对象被赋值,不如直接在接口类里禁用赋值运算符:
最直接的方案:在接口类里禁用赋值
如果你的目标是彻底禁止任何外部代码给接口对象赋值,直接在Interface1基类里删掉赋值运算符就行:
class Interface1 { public: // ... 你的接口方法 ... // 禁用赋值 Interface1& operator=(const Interface1&) = delete; Interface1& operator=(Interface1&&) = delete; };
这样不管通过哪个引用,都没法给对象赋值,同时完全不影响非const成员方法的调用。
内容的提问来源于stack exchange,提问作者Russ Schultz

