lundi 27 juin 2016

Confusing error messages with named rvalue references

Consider the following:

struct my_type {};

my_type make_my_type() { return my_type{}; }

void func(my_type&& arg) {}

int main()
{
    my_type&& ref = make_my_type();

    func(ref);
}

Needless to say, this code doesn't compile. I realise that I need to use std::move() in the second function call, but for the purposes of understanding I want to consider the code as it is.

Attempting to compile the above, Clang 3.5 tells me:

error: no matching function for call to 'func'

note: candidate function not viable: no known conversion from 'my_type' to 'my_type &&' for 1st argument void func(my_type&&) {}

While g++ 4.9 says something almost identical:

error: cannot bind 'my_type' lvalue to 'my_type&&'

note: initializing argument 1 of 'void func(my_type&&)'

These error messages have me rather confused, because while ref is certainly an lvalue, its type is still my_type&&... isn't it?

I'm trying to understand exactly what's going on here, so I'm wondering which (if any) of the following are true:

  • Since only rvalues can be bound to rvalue references, and ref is an lvalue, it cannot be bound to arg. The error messages from both Clang and g++ are misleading in claiming that ref is a (non-reference) my_type that "cannot be converted".

  • Because it is an lvalue, ref is treated for the purposes of overload resolution as a non-reference my_type, despite its actual type being my_type&&. The error messages from Clang and g++ are misleading because they are displaying the type as used internally for function matching, not the real type of ref.

  • In the body of main(), the type of ref is plain my_type, despite the fact I explicitly wrote my_type&&. So the error messages from the compilers are accurate, and it is my expectation that is wrong. This doesn't seem to be the case however, since

    static_assert(std::is_same<decltype(ref), my_type&&>::value, "");
    

    passes.

  • There is some other magic going on that I haven't considered.

Just to repeat, I know that the solution is to use std::move() to convert the rref back into an rvalue; I'm looking for an explanation of what's going on "behind the scenes".

Aucun commentaire:

Enregistrer un commentaire