8

给定以下转换运算符

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 类似),但行为不同。

任何解释或解决方法?

我的动机是又一个 '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)。

4

1 回答 1

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

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

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

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

这与调用同时具有 && 和 const & 限定的重载的成员函数不同,因为在这种情况下,重载解析将引用绑定从右值参数排序到隐含对象参数

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

于 2014-04-30T16:50:23.527 回答