C++ Logo

std-proposals

Advanced search

Re: Bringing consistency to implicit conversion by ref-qualified conversion functions

From: Hani Deek <hani_deek_at_[hidden]>
Date: Wed, 30 Dec 2020 22:15:37 +0000
I believe the compilers will accept these examples when the types in question are convertible to each other. The type int is implicitly convertible to float and vice versa, but the same is untrue for int*/float* and A/B in your examples. Jim x suggested an explanation for this behavior in this post https://lists.isocpp.org/std-discussion/2020/12/0941.php. The complexity of his explanation is one motive behind my proposal.

I was designing a wrapper class and wanted the wrapped value to be accessed through an rvalue-reference in case the wrapper is a prvalue, something like the following.

template<typename T>
struct Wrapper
{
        T WrappedValue;

        operator T &() & { return WrappedValue; }
        operator const T &() const & { return WrappedValue; }
        operator T &&() && { return static_cast<T &&>(WrappedValue); }
};

I want to propose a specific wording change to the standard, but I will do that after I see the responses here. If I get positive feedback, I will try to formulate the proposal. Probably I will need to ask others for help with that. I am not fluent in the language of the standard.

________________________________
On Wed, Dec 30, 2020 at 4:27 PM Hani Deek via Std-Proposals <std-proposals_at_[hidden]<mailto:std-proposals_at_[hidden]>> wrote:

One area in C++ where there is significant complexity and inconsistency is the way compilers handle ref-qualified implicit conversion functions.

I believe that most C++ users who put ref-qualifiers on implicit conversion functions expect those ref-qualifiers to have the same effect in all situations. Probably few know that the ref-qualifiers will have different effects in different situations, as illustrated by the examples below.

https://godbolt.org/z/8a9Tcc
struct S {
    operator float() &&;
    operator int() const&;

    operator int*() &&;
    operator float*() const&;

    operator A() &&;
    operator B() const&;
};

void prim(float);
void prim(int);
void ptr(int*);
void ptr(float*);
void obj(A);
void obj(B);

void test() {
    prim(S()); // OK
    ptr(S()); // error, ambiguous
    obj(S()); // error, ambiguous
}

Agreed, this doesn't seem to make any sense. But is this perhaps just an implementation bug (in all these compilers)? What possible reason could there be for the `int/float` case being unambiguous but the `int*/float*` case being ambiguous? Both cases involve primitive types. Do you understand why the implementations are doing what they're doing here? because I definitely don't.

If you're proposing a specific wording change to the Standard — as opposed to just detecting a weird bug in every implementation, which I haven't completely ruled out — then it'll help your case to say why you want to have a class type with multiple ref-qualified conversion operators to different types. Is this a real-world situation you're running into, or just a bit of trivia?

Also, for the record, ref-qualified conversion operators are superficially relevant to P1155 "More implicit moves" (adopted in C++20) / P2266 "Simpler implicit move" (forthcoming, targeting C++2b), but Hani's issue is orthogonal and unrelated to P1155/P2266. P1155/P2266 deal with "implicit move" scenarios like
https://godbolt.org/z/MnhKEK
whereas Hani's scenarios involve situations where the thing being converted is legitimately a prvalue already, no "implicit move" needed.

my $.02,
–Arthur

Received on 2020-12-30 16:15:42