操作中的显式ref-qualified转换操作符模板 [英] Explicit ref-qualified conversion operator templates in action

查看:254
本文介绍了操作中的显式ref-qualified转换操作符模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定以下转换运算符

  struct A 
{
template< typename T&显式运算符T& ()&&;
template< typename T>显式运算符T& ()& ;;
template< typename T>显式运算符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.错误:没有从A到B的可行转换
B& bl(a1); // 4. OK
const B& bz(a1); // 5. OK
const B& bc(ac); // 6. OK

B cm(std :: move(a)); // 7.错误:调用B的构造函数b ambiguous
B ct(A {}); // 8. error:调用B的构造函数b ambiguous
B cr(ar); // 9. OK

特别是,1似乎与3相同,



任何解释或解决方法?



动机是又一个任何,我最终必须使所有转换操作符显式 std :: is_constructible std :: is_convertible 可以避免类型trait的问题,

对不起,请忽略3和9,我的错误(感谢Kerrek SB)。但7和8仍然是问题。



EDIT 2 。只要注意到

  B cm = std :: move(a); 
B ct = A {};如果转换操作符不显式,则



< >。所以这是显式进来:最初我的样本使用复制初始化,当我切换到显式使用直接初始化。

>




  B cm(std :: move ); // 7.错误:调用B的构造函数b ambiguous 
B ct(A {}); // 8.错误:调用B的构造函数b ambiguous

这两种情况是一样的:对于类型A的rvalue参数。



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


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


这不同于调用具有&&和const& -qualified重载的成员函数,因为在这种情况下,重载解决是排名标准转换序列S1是比标准转换序列S2更好的转换序列,如果S1是标准转换序列S2,则标准转换序列S1是比标准转换序列S2更好的转换序列。和S2是引用绑定(8.5.3),并且不是指没有ref-qualifier声明的非静态成员函数的隐式对象参数,S1将右值引用绑定到右值,而S2绑定左值引用。 p>


Given the following conversion operators

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

struct B {};

I would expect the following conversions to be all valid, yet some give compile errors (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

In particular, 1 appears to be identical to 3, and almost identical to 2 (similarly for 7 to 9, 8), yet behave differently.

Any explanation or workaround?

My motivation is Yet another 'any', where I eventually had to make all conversion operators explicit to avoid problems with type traits like std::is_constructible, std::is_convertible, then I bumped into new problems.

EDIT Sorry, please ignore 3 and 9, my mistake (thanks Kerrek SB). Yet 7 and 8 remain as problems. Also, explicit appears to be irrelevant after all, sorry again.

EDIT 2 Just noticed that

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

are valid if the conversion operators are not explicit. So that's where explicit comes in: initially my samples used copy-initialization, and when I switched to explicit I had to use direct-initialization. Then this problem came up (cases 7 and 8).

解决方案

Yet 7 and 8 remain as problems

B        cm(std::move(a));  // 7. error: call to constructor of B ambiguous
B        ct(A{});           // 8. error: call to constructor of B ambiguous

The two cases are the same: direct initialization with rvalue argument of type A.

The candidate functions for direct initialization are all constructors, and in this case, both copy constructor B::B(const B&) and move constructor B(B&&) are viable, since there is an implicit conversion from rvalue A to both const B& and to B&&. Overload resolution cannot decide between these two constructors because calling either one requires a user-defined conversion directly into the parameter type, and user-defined conversion sequences are only ranked by the second standard conversion:

13.3.3.2/3[over.ics.rank]: User-defined conversion sequence U1 is a better conversion sequence than another user-defined conversion sequence U2 if they contain the same user-defined conversion function ... and the second standard conversion sequence of U1 is better than the second standard conversion sequence of U2."

This is different from calling a member function that has both && and const &-qualified overloads because in that case, overload resolution is ranking the reference bindings from rvalue argument to implict object parameter accoring to

Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.

这篇关于操作中的显式ref-qualified转换操作符模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆