为何const Type&&无法转换为Type&&?移动构造<const T, T>类型pair时遇编译错误
为何const Type&&无法转换为Type&&?移动构造<const T, T>类型pair时遇编译错误
嘿,这个问题我帮你捋清楚,你踩的是C++里右值引用和const限定符的一个经典坑~
首先先讲核心规则:const T&&(const右值引用)是不能隐式转换成T&&(非const右值引用)的。这很好理解:右值引用设计出来就是为了允许我们修改临时对象(毕竟移动语义就是要“偷”临时对象的资源),但const T&&指向的是一个不能被修改的临时对象,要是能转成T&&,那不就等于允许我们修改const对象了吗?C++绝不允许这种违反const语义的不安全转换。
接下来结合你的代码场景拆解问题:
你定义的NeitherDefaultNorCopyConstructible类,删除了默认构造和拷贝构造,但没有显式定义或默认移动构造函数——因为你自定义了一个接受Special&&的构造函数,编译器不会自动为你生成默认的移动构造函数。
当你尝试移动构造pair<const NeitherDefaultNorCopyConstructible, NeitherDefaultNorCopyConstructible>时,问题出在pair的第一个成员(const NeitherDefaultNorCopyConstructible)上:
- 对于const修饰的成员,编译器要完成“移动构造”,本质上只能尝试两种方式:要么调用类的移动构造函数,要么退而求其次用拷贝构造。
- 但你的类没有移动构造函数,拷贝构造又被删了;退一步说,就算你有移动构造函数(比如
NeitherDefaultNorCopyConstructible(NeitherDefaultNorCopyConstructible&&)),源pair的第一个元素的右值引用是const NeitherDefaultNorCopyConstructible&&,它根本绑定不到NeitherDefaultNorCopyConstructible&&这种非const的右值引用参数上——这就回到了最开始的核心规则,const右值引用不能转成非const的。
所以最终的结果就是,编译器找不到合法的构造方式来初始化这个const成员,自然就报编译错误了。
怎么解决?
根据你的需求有两种方向:
- 如果可以去掉第一个元素的const限定:直接用
pair<NeitherDefaultNorCopyConstructible, NeitherDefaultNorCopyConstructible>,这样移动构造时两个元素都能用非const右值引用,只要你给NeitherDefaultNorCopyConstructible添加默认的移动构造函数(NeitherDefaultNorCopyConstructible(NeitherDefaultNorCopyConstructible&&) = default;),就能正常编译。 - 必须保留第一个元素的const限定:那你得接受“const对象的移动本质是拷贝”这个事实,要么恢复拷贝构造函数(当然这可能违背你的设计初衷),要么重新考量你的类型设计——毕竟const成员的移动语义意义不大,更多是逻辑上的矛盾。
给你补全修正后的代码示例(去掉const限定的情况):
#include <iostream> #include <utility> struct Special { int x = 0; explicit Special(int x): x(x) {} Special(const Special&) = delete; Special& operator=(const Special&) = delete; Special(Special&&) = default; Special& operator=(Special&&) = default; }; struct NeitherDefaultNorCopyConstructible { Special x; NeitherDefaultNorCopyConstructible() = delete; NeitherDefaultNorCopyConstructible(const NeitherDefaultNorCopyConstructible&) = delete; NeitherDefaultNorCopyConstructible& operator=(const NeitherDefaultNorCopyConstructible&) = delete; // 自定义构造函数 NeitherDefaultNorCopyConstructible(Special&& x): x(std::move(x)) {} // 显式默认移动构造函数 NeitherDefaultNorCopyConstructible(NeitherDefaultNorCopyConstructible&&) = default; NeitherDefaultNorCopyConstructible& operator=(NeitherDefaultNorCopyConstructible&&) = default; }; int main() { auto p1 = std::make_pair(NeitherDefaultNorCopyConstructible(Special{1}), NeitherDefaultNorCopyConstructible(Special{2})); auto p2 = std::move(p1); // 现在可以正常移动构造了 std::cout << p2.first.x.x << " " << p2.second.x.x << std::endl; return 0; }
备注:内容来源于stack exchange,提问作者Riabov Vladimir
相关产品推荐
相关产品推荐

