运行中明确的ref-qualified转换运算符模板

iav*_*avr 8 c++ rvalue-reference conversion-operator c++11 ref-qualifier

给出以下转换运算符

struct A
{
    template<typename T> explicit operator T&&       () &&;
    template<typename T> explicit operator T&        () &;
    template<typename T> explicit operator const T&  () const&;
};

struct B {};
Run Code Online (Sandbox Code Playgroud)

我希望以下转换都是有效的,但有些会给出编译错误(实例):

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
Run Code Online (Sandbox Code Playgroud)

特别地,1看起来与3相同,并且几乎与2相同(类似地对于7到9,8),但表现不同.

任何解释或解决方法?

我的动机是另一种"任意",在那里我最终不得不让所有的转换运营商explicit,以避免与类型特征类似的问题std::is_constructible,std::is_convertible的话,我碰到了新的问题.

编辑对不起,请忽略3和9,我的错误(谢谢Kerrek SB).然而,7和8仍然是问题.而且,explicit毕竟似乎无关紧要,再次抱歉.

编辑2刚刚注意到了

B        cm = std::move(a);
B        ct = A{};
Run Code Online (Sandbox Code Playgroud)

如果转换运算符不是,则有效explicit.所以explicit它就在哪里:最初我的样本使用了复制初始化,当我切换到explicit我必须使用直接初始化时.然后出现了这个问题(案例7和8).

Cub*_*bbi 6

然而 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
Run Code Online (Sandbox Code Playgroud)

这两种情况是一样的:使用类型 A 的右值参数直接初始化。

直接初始化的候选函数都是构造函数,在这种情况下,复制构造函数B::B(const B&)和移动构造函数B(B&&)都是可行的,因为存在从右值 A 到 bothconst B&和 to的隐式转换B&&。重载解析无法在这两个构造函数之间做出决定,因为调用任一构造函数都需要将用户定义的转换直接转换为参数类型,而用户定义的转换序列仅按第二个标准转换排序:

13.3.3.2/3[over.ics.rank]: 用户自定义转换序列 U1 是比另一个用户自定义转换序列 U2 更好的转换序列,如果它们包含相同的用户定义转换函数...并且 U1 的第二个标准转换序列优于另一个用户定义的转换序列 U2 U2。”

这与调用具有 && 和 const & 限定重载的成员函数不同,因为在这种情况下,重载解析是根据以下条件对从右值参数到隐式对象参数的引用绑定进行排名

标准转换序列 S1 是比标准转换序列 S2 更好的转换序列,如果 S1 和 S2 是引用绑定(8.5.3)并且都没有引用没有引用限定符声明的非静态成员函数的隐式对象参数,并且 S1将右值引用绑定到右值,S2 绑定左值引用。