Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicit ref-qualified conversion operator templates in action

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).

like image 806
iavr Avatar asked Apr 30 '14 00:04

iavr


People also ask

What is explicit cast in C++?

C++ Explicit Conversion. When the user manually changes data from one type to another, this is known as explicit conversion. This type of conversion is also known as type casting.

What is C++ conversion operator?

Conversion Operators in C++ C++ supports object oriented design. So we can create classes of some real world objects as concrete types. Sometimes we need to convert some concrete type objects to some other type objects or some primitive datatypes. To make this conversion we can use conversion operator.

What are the types of type conversion?

There are two types of conversion: implicit and explicit. The term for implicit type conversion is coercion. Explicit type conversion in some specific way is known as casting. Explicit type conversion can also be achieved with separately defined conversion routines such as an overloaded object constructor.

What is type conversion in C++ with example?

Finding the sum of two variables, one of int type and the other of the float type is a good example to demonstrate the use of type conversion. To get the sum of the two variables, you have to convert the int variable to float .


1 Answers

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.

like image 171
Cubbi Avatar answered Oct 03 '22 00:10

Cubbi