操作中的显式ref限定的转换运算符模板

Explicit ref-qualified conversion operator templates in action

本文关键字:转换 运算符 ref 操作      更新时间:2023-10-16

给定以下转换运算符

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_constructiblestd::is_convertible等类型特征的问题,然后我遇到了新的问题。

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

编辑2刚刚注意到

B        cm = std::move(a);
B        ct = A{};

如果转换运算符不是explicit,则是有效的。这就是explicit的作用所在:最初我的样本使用复制初始化,当我切换到explicit时,我必须使用直接初始化然后这个问题出现了(案例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&B&&的隐式转换。重载解析无法在这两个构造函数之间做出决定,因为调用其中任何一个都需要用户定义的直接转换为参数类型的转换,并且用户定义的转换序列仅按第二个标准转换进行排序

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

这与调用同时具有&amp;以及const&amp-限定重载,因为在这种情况下,重载解析是根据对从右值参数到隐含对象参数的引用绑定进行排序

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