给出以下转换运算符
struct A
{
template<typename T> explicit operator T&& () &&;
template<typename T> explicit operator T& () &;
template<typename T> explicit operator const T& () const&;
};
struct B {};
我希望以下转换都是有效的,但有些会给出编译错误(live example):
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),但表现不同。
任何解释或解决方法?
我的动机是Yet another 'any',我最终必须制作所有转换操作符explicit
以避免类似std::is_constructible
,std::is_convertible
等类型特征的问题,然后我遇到了新的问题问题。
编辑抱歉,请忽略3和9,我的错误(感谢Kerrek SB)。然而,7和8仍然是问题。此外,explicit
似乎无关紧要,再次抱歉。
编辑2 刚刚注意到
B cm = std::move(a);
B ct = A{};
如果转换运算符不是explicit
,则有效。这就是explicit
所在的地方:最初我的样本使用了复制初始化,当我切换到explicit
时,我不得不使用直接初始化。 然后出现了这个问题(案例7和8)。
答案 0 :(得分:4)
然而,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的rvalue参数直接初始化。
直接初始化的候选函数都是构造函数,在这种情况下,复制构造函数B::B(const B&)
和移动构造函数B(B&&)
都是可行的,因为存在从rvalue A到{{{}的隐式转换。 1}}和const B&
。重载决策不能在这两个构造函数之间决定,因为调用其中一个需要用户定义的转换直接进入参数类型,而用户定义的转换序列仅按第二个标准转换进行排序:
B&&
:用户定义的转换序列U1是比另一个用户定义的转换序列U2更好的转换序列,如果它们包含相同的用户定义转换函数...并且U1的第二个标准转换序列是优于U2的第二个标准转换序列。&#34;
这与调用同时具有&amp;&amp;和&amp;和const&amp; -qualified重载,因为在这种情况下,重载决策将从rvalue参数到implict对象参数的引用绑定排序为
标准转换序列S1是比标准转换序列S2更好的转换序列,如果S1和S2是引用绑定(8.5.3)并且都没有引用没有ref-qualifier声明的非静态成员函数的隐式对象参数,并且S1将右值引用绑定到右值,S2绑定左值引用。