左值引用运算符与赋值运算符的优先级问题排查
编译器优先用默认赋值运算符而非左值引用转换运算符的原因及解决办法
为什么会出现这个现象?
当执行w = 1这类赋值操作时,编译器的重载决议逻辑优先级如下:
- 若未显式声明
operator=(T),编译器会自动为wrapper生成默认拷贝赋值运算符(wrapper& operator=(const wrapper&))。此时1可以通过wrapper的非explicit构造函数,隐式转换为wrapper<int>临时对象,刚好匹配这个拷贝赋值运算符——这条路径合法,因此编译器会优先选择它,完全不会考虑将w转换为int&再赋值的路径。 - 当取消注释
wrapper<T> operator=(T) = delete;时,用户主动声明了该赋值运算符,编译器就不会再生成默认的拷贝赋值运算符。此时w=1会优先匹配这个被删除的operator=(int),直接导致编译失败,编译器也不会去尝试转换w为int&的路径。
如何让左值引用运算符被隐式调用?
核心思路是切断T类型隐式转换为wrapper的路径,迫使编译器只能选择转换wrapper为T&的逻辑。最实用的方案是将wrapper的构造函数声明为explicit:
#include <iostream> template <typename T> struct wrapper { private: T t; public: explicit wrapper(T _t) : t(_t) {} // 关键:添加explicit修饰 // wrapper<T> operator=(T) = delete; // 可按需保留或删除,不影响最终效果 operator const T&() const { return t; } operator T&() { return t; } }; int main() { wrapper<int> w(0); std::cout << w << std::endl; w = 1; // 现在会触发operator T&(),直接给内部的t赋值 std::cout << w << std::endl; }
修改后,1无法隐式转换为wrapper<int>,编译器只能将w转换为int&,进而调用内置的int赋值运算符,自然就触发了你定义的左值引用转换运算符。
如果你需要彻底禁用所有wrapper的成员赋值操作(包括对象之间的拷贝/移动赋值),也可以显式删除相关运算符,同样能达到效果:
template <typename T> struct wrapper { private: T t; public: wrapper(T _t) : t(_t) {} wrapper<T>& operator=(const wrapper<T>&) = delete; // 禁用拷贝赋值 wrapper<T>& operator=(wrapper<T>&&) = delete; // 禁用移动赋值 wrapper<T> operator=(T) = delete; // 禁用T类型赋值 operator const T&() const { return t; } operator T&() { return t; } };
不过这种方式会禁止wrapper对象之间的赋值操作,需根据实际需求选择。
内容的提问来源于stack exchange,提问作者ryan.walton.256
相关产品推荐
相关产品推荐

