Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the + operator default to rvalue overload if using auto?

Tags:

c++

#include <iostream>

struct Obj{ Obj(){} };
struct Obj2{ Obj2(){} };

// option 1
Obj operator+(Obj const& x0, Obj const& x1) { printf("%s\n", "+(const&, const&)"); return Obj{}; }
Obj operator+(Obj const& x0, Obj&& x1) { printf("%s\n", "+(const&, &&)"); return Obj{}; }
Obj operator+(Obj&& x0, Obj const& x1) { printf("%s\n", "+(&&, const&)"); return Obj{}; }
Obj operator+(Obj&& x0, Obj&& x1) { printf("%s\n", "+(&&, &&)"); return Obj{}; }

//option 2
// Obj2 operator+(auto const& x0, auto const& x1) { printf("%s\n", "+(const&, const&)"); return Obj2{}; }
// Obj2 operator+(auto const& x0, auto&& x1) { printf("%s\n", "+(const&, &&)"); return Obj2{}; }
// Obj2 operator+(auto&& x0, auto const& x1) { printf("%s\n", "+(&&, const&)"); return Obj2{}; }
// Obj2 operator+(auto&& x0, auto&& x1) { printf("%s\n", "+(&&, &&)"); return Obj2{}; }

int main()
{
    Obj x0;
    Obj x1{ x0 };
    auto x2 = x0 + x1;
}

If I choose option 1, I get the expected call of (const&, const&) since x0 and x1 are clearly lvalues. BUT, if I go for option 2, it calls the (&&, &&) overload and thinks that x0 and x1 are rvalues?! Why is this happening? Serious question. Shouldn't it go for the more constrained version anyway and ignore option 2? Even so, should it even matter what option I go for when x0 and x1 should ALWAYS be lvalues?

like image 283
Saitama10000 Avatar asked Nov 28 '25 17:11

Saitama10000


1 Answers

BUT, if I go for option 2, it calls the (&&, &&) overload and thinks that x0 and x1 are rvalues?! Why is this happening?

This is because in option 2, the overloaded operators are actually "templated"(aka abbreviated function templates) and behave like you have template<typename T, typename U> operator+( T&&, U&&) etc which has forwarding references and among all the four overloads, the last overloaded version Obj2 operator+(auto&& x0, auto&& x1); is the best match for the call x0 + x1.


For example, Obj2 operator+(auto&& x0, auto&& x1) is basically the same as(or equivalent to writing)

template<typename T, typename U>
Obj2 operator+(T&& x0, U&& x1)

This means that what you have actually is:

template<typename T, typename U>
Obj2 operator+(T const& x0, U const& x1);
template<typename T, typename U>
Obj2 operator+( const& x0, auto&& x1);
template<typename T, typename U>
Obj2 operator+(T&& x0, U const& x1);
template<typename T, typename U>
Obj2 operator+(T&& x0, U&& x1); //this is best match for x0 + x1
like image 185
Anoop Rana Avatar answered Dec 01 '25 06:12

Anoop Rana