c++constantsoperator-overloadingc++14ref-qualifier

Why do ref-qualifier together with cv-qualifier on operator overloading allow rvalue assignment?


Adding a ref-qualifier to an operator will remove the possibility to do rvalue assignment

for example, compiling the following with g++ -std=c++14 bar.cpp && ./a.out

#include <cstdio>

struct foo
{
    void operator+=(int x) & { printf("%d\n", x+2); }  
};

int main()
{
    foo() += 10;
}

will give you

$ g++ -std=c++14 bar.cpp && ./a.out
bar.cpp: In function ‘int main()’:
bar.cpp:14:14: error: passing ‘foo’ as ‘this’ argument discards qualifiers [-fpermissive]
   14 |     foo() += 10;
      |              ^~
bar.cpp:6:10: note:   in call to ‘void foo::operator+=(int) &’
    6 |     void operator+=(int x) & { printf("%d\n", x+2); }
      |          ^~~~~~~~

you can of course "fix" this by adding an explicit &&

#include <cstdio>

struct foo
{
    void operator+=(int x) & { printf("%d\n", x+2); }  
    void operator+=(int x) && { printf("%d\n", x+3); }  
};

int main()
{
    foo() += 10;
}

output

$ g++ -std=c++14 bar.cpp && ./a.out
13

But adding a const & will also let you call += on the struct instance.

#include <cstdio>

struct foo
{
    void operator+=(int x) & { printf("%d\n", x+2); }  
    void operator+=(int x) const & { printf("%d\n", x+4); }  
};

int main()
{
    foo() += 10;
}

output

$ g++ -std=c++14 bar.cpp && ./a.out
14

To "fix" this, the const && must be explicitly deleted

#include <cstdio>

struct foo
{
    void operator+=(int x) & { printf("%d\n", x+2); }  
    void operator+=(int x) const & { printf("%d\n", x+4); }  
    void operator+=(int x) const && = delete;
};

int main()
{
    foo() += 10;
}

output

$ g++ -std=c++14 bar.cpp && ./a.out
bar.cpp: In function ‘int main()’:
bar.cpp:14:14: error: use of deleted function ‘void foo::operator+=(int) const &&’
   14 |     foo() += 10;
      |              ^~
bar.cpp:9:10: note: declared here
    9 |     void operator+=(int x) const && = delete;
      |          ^~~~~~~~

Why is this? Why do adding the ref-qualifier implicitly remove the rvalue assignment? But adding a cv-qualifier together with ref-qualifier seem to implicitly add the rvalue assignment?

I'm sure I'm missing something obvious here. But Google-Wan Kenobi doesn't seem to be able to help me understand.


Solution

  • Because rvalues could be bound to lvalue-reference to const. Just same as the following code:

    foo& r1 = foo();       // invalid; rvalues can't be bound to lvalue-reference to non-const
    const foo& r2 = foo(); // fine; rvalues can be bound to lvalue-reference to const
    

    BTW: The overload qualified with rvalue-reference wins in overload resolution when calling on rvalues. That's why you mark it as delete explicitly works as expected.