能否配置用户自定义运算符决议顺序?编译失败案例分析
用户自定义运算符的决议顺序问题分析与解决方案
是否可以配置用户自定义运算符的决议顺序?请考虑以下代码:
#include <memory> #include <utility> using P = std::unique_ptr<int>; struct S{ P p; operator P() && { return std::move(p); } operator const P&() const { return p; } }; S s{std::make_unique<int>()}; P p; p = std::move(s);
这段代码编译失败,报错信息如下:
In file included from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/memory:76, from <source>:1: /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/unique_ptr.h:406:19: note: candidate: 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = int; _Dp = std::default_delete<int>]' 406 | unique_ptr& operator=(unique_ptr&&) = default; | ^~~~~~~~ /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/unique_ptr.h:515:19: note: candidate: 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]' (deleted) 515 | unique_ptr& operator=(const unique_ptr&) = delete;
编译失败原因似乎是已删除的拷贝赋值运算符与默认的移动赋值运算符存在歧义。请问编译器为何无法解析该操作?是否有办法定义运算符使代码正常工作?
编译器无法解析的核心原因
执行p = std::move(s)时,编译器面临两个可行的转换路径:
- 路径1:通过
operator P() &&将右值std::move(s)转换为P&&,匹配unique_ptr的移动赋值运算符operator=(P&&)。 - 路径2:通过
operator const P&() const将右值std::move(s)转换为const P&,匹配unique_ptr已删除的拷贝赋值运算符operator=(const P&)。
C++中右值可以绑定到const左值引用,因此这两个转换运算符均为合法候选。而重载决议规则中,这两个用户定义转换的优先级完全相同,编译器无法判定哪一个更符合意图,最终触发歧义报错。
修复方案
方案1:为const转换运算符添加左值限定
修改const版本的转换运算符,限制它仅能被左值S对象调用,这样右值的std::move(s)会唯一匹配到移动版转换运算符:
struct S{ P p; operator P() && { return std::move(p); } operator const P&() const & { return p; } // 添加&左值限定 };
此方案从根源上切断了右值匹配const转换的可能,彻底消除歧义。
方案2:显式指定转换路径(不推荐)
如果不想修改结构体定义,可以在赋值时显式指定转换类型,强制编译器选择移动路径:
p = static_cast<P&&>(std::move(s));
这种方式会破坏代码的可读性,仅作为临时 workaround 使用。
内容的提问来源于stack exchange,提问作者Deev
相关产品推荐
相关产品推荐

