为何含用户自定义字面量(UDL)的表达式未调用移动构造/赋值?
禁用拷贝的类S链式调用构造问题解析
问题背景
定义类S时禁用了拷贝构造和拷贝赋值运算符,保留移动构造与移动赋值;重载了operator<<并添加用户自定义字面量_S。出现以下现象:
auto s1 = "Test"_S << 1 << 2 << 3;报错,提示调用已删除的拷贝构造函数auto s2 = "123"_S;可正常运行
问题1:为何s1未从临时表达式移动赋值?
核心原因是链式调用的operator<<返回值类型导致表达式结果为左值,无法触发移动构造:
- 如果你的
operator<<重载为返回左值引用(比如S& operator<<(S& lhs, int val)),那么"Test"_S << 1返回的是原临时对象的左值引用,后续的<<2、<<3也都是对这个左值的操作,最终整个表达式的结果是左值。 - 当用
auto s1 = ...初始化时,左值无法触发移动构造(移动构造要求右值),编译器会尝试调用拷贝构造来复制这个左值,而你的拷贝构造已被禁用,因此报错。
解决办法是调整operator<<的重载逻辑:
- 改为传值并返回临时对象:
S operator<<(S lhs, int val),修改传入的lhs后返回,这样每次调用都会生成右值临时对象,最终表达式结果是右值,s1会通过移动构造初始化。 - 为右值重载专属版本:
S&& operator<<(S&& lhs, int val),修改右值lhs后返回右值引用,确保整个链式调用的结果始终是右值,触发移动构造。
问题2:s2是否原地构造避免了移动赋值?
是的,这是C++标准规定的**拷贝省略(Copy Elision)**优化:
auto s2 = "123"_S;中,"123"_S会生成一个临时对象,编译器会直接在s2的内存地址上构造这个对象,完全跳过移动(或拷贝)构造的调用。- 这种优化不需要依赖移动构造或拷贝构造,即使两者都被禁用(只要对象的构造函数可行),编译器也能直接完成原地构造,因此代码可以正常运行。
内容的提问来源于stack exchange,提问作者PaperBirdMaster
相关产品推荐
相关产品推荐

