给定以下转换运算符
struct A
{
template<typename T> explicit operator T&& () &&;
template<typename T> explicit operator T& () &;
template<typename T> explicit operator const T& () const&;
};
struct B {};
我希望以下转换都是有效的,但有些会给出编译错误(活生生的例子):
A a;
A&& ar = std::move(a);
A& al = a;
const A& ac = a;
B&& bm(std::move(a)); // 1. OK
B&& bt(A{}); // 2. OK
B&& br(ar); // 3. error: no viable conversion from A to B
B& bl(al); // 4. OK
const B& bz(al); // 5. OK
const B& bc(ac); // 6. OK
B cm(std::move(a)); // 7. error: call to constructor of B ambiguous
B ct(A{}); // 8. error: call to constructor of B ambiguous
B cr(ar); // 9. OK
特别是,1 看起来与 3 相同,并且与 2 几乎相同(7 到 9、8 也类似),但行为却不同。
有什么解释或解决方法吗?
我的动机是还有一个“任何”,我最终不得不进行所有转换运算符explicit
以避免类型特征的问题,例如std::is_constructible
, std::is_convertible
,然后我又遇到了新的问题。
EDIT抱歉,请忽略 3 和 9,我的错误(感谢 Kerrek SB)。然而,7 和 8 仍然是问题。还,explicit
看来终究是无关紧要的,再次抱歉。
EDIT 2刚刚注意到
B cm = std::move(a);
B ct = A{};
如果转换运算符不是有效的explicit
。所以这就是地方explicit
进来:最初我的样本使用复制初始化,当我切换到explicit
我必须使用直接初始化。Then这个问题就出现了(案例7和8)。
然而7和8仍然是问题
B cm(std::move(a)); // 7. error: call to constructor of B ambiguous
B ct(A{}); // 8. error: call to constructor of B ambiguous
这两种情况是相同的:使用 A 类型的右值参数直接初始化。
直接初始化的候选函数都是构造函数,在本例中,都是复制构造函数B::B(const B&)
并移动构造函数B(B&&)
是可行的,因为存在从右值 A 到两者的隐式转换const B&
and to B&&
。重载解析无法在这两个构造函数之间做出决定,因为调用任一构造函数都需要将用户定义的转换直接转换为参数类型,并且用户定义的转换序列仅按第二标准转换:
13.3.3.2/3[over.ics.rank]
:如果用户定义的转换序列 U1 包含相同的用户定义的转换函数,则它们是比另一个用户定义的转换序列 U2 更好的转换序列...并且 U1 的第二个标准转换序列优于 U1 的第二个标准转换序列U2。”
这与调用同时具有 && 和 const & 限定重载的成员函数不同,因为在这种情况下,重载解析会根据以下条件对从右值参数到隐式对象参数的引用绑定进行排序
如果 S1 和 S2 是引用绑定 (8.5.3),并且都不引用未使用 ref 限定符声明的非静态成员函数的隐式对象参数,并且 S1,则标准转换序列 S1 是比标准转换序列 S2 更好的转换序列将右值引用绑定到右值,S2 绑定左值引用。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)