4

Let's say we have member class with two member functions defined as follows:

class SomeClass
{
private:
  int val = {};
public:

  const int getVarLRef() & {
    return val;
  }
  const int getVarCLRef() const& {
    return val;
  }
};

int main()
{
  auto var1 = SomeClass().getVarCLRef();
  auto var2 = SomeClass().getVarLRef();
  return 0;
}

I not quite understand what is the difference between const& and &. Why it works with getVarCLRef if we specified this function as const&? Shouldn't it be allowed to be invoked only with lvalues?

getVarLRef, on the other hand, works just fine and fails to compile in this case as expected.

I use C++11 and gcc 7.3.0

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
toozyfuzzy
  • 1,080
  • 1
  • 9
  • 20
  • 1
    Note that `auto` in your example translates to `int`, not to `int&`. If you want it to be a reference type, you should use `auto&` instead. – Denis Sheremet Nov 29 '19 at 09:56
  • Interesting observation. I guess the answers for https://stackoverflow.com/questions/23011532/const-reference-qualifier-on-a-member-function and https://stackoverflow.com/questions/21861148/what-does-the-single-ampersand-after-the-parameter-list-of-a-member-function-dec are not really correct (most of them talk about `&` requiring the object to be an lvalue)... – Max Langhof Nov 29 '19 at 10:53
  • Side note: Returning by const value is pretty meaningless, the value will be copied anyway (copy elision occuring doen't change anything about), that just makes the code harder to read. In worst case, you just prevent being able to using the object directly (`x.get().foo();`), still you can almost *always* (since C++17; due to [guaranteed copy elision](https://jonasdevlieghere.com/guaranteed-copy-elision/)) circumvent by `auto y = x.get(); y.foo();`. – Aconcagua Nov 29 '19 at 12:03

2 Answers2

2

Shouldn't it be allowed to be invoked only with lvalues?

Because rvalue could be bound to lvalue-reference to const too. Just as the following code works.

const SomeClass& r = SomeClass();

On the other hand, rvalue can't be bound to lvalue-reference to non-const, then the invocation of getVarLRef fails as you expected.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Don't we call this method before the object is bound to the reference and is still rvalue? – toozyfuzzy Nov 29 '19 at 09:16
  • @toozyfuzzy I'm not sure what you meant.. In theory it's same as `const int getVarCLRef(const SomeClass&); getVarCLRef(SomeClass{});`; i.e. passing an rvalue to function taking lvalue-reference to `const`. – songyuanyao Nov 29 '19 at 09:22
  • @toozyfuzzy Try: `SomeClass& s = SomeClass();` for comparison. GCC's error message is much clearer about that... – Aconcagua Nov 29 '19 at 09:25
  • @songyuanyao what 'const&' even means in context of member function definition as specified in question? When one defines member function like that, what is expected result? – toozyfuzzy Nov 29 '19 at 09:37
  • 2
    @toozyfuzzy I can't think of any. Providing overloaded member functions with `&` and `&&` qualifies seems much more natural to me. – songyuanyao Nov 29 '19 at 10:01
  • 1
    @toozyfuzzy In your concrete case, it looks pretty much as you wouldn't need those qualifiers at all, just have `const` or nothing at all. Then the const version will be selected on constant objects while the other one on mutable ones. Qualifying with & and && makes a distinction: Will you be calling the function on an lvalue or on an rvalue reference? Maybe your class might prefer to behave differently? – Aconcagua Nov 29 '19 at 10:40
  • 1
    Let's assume your function returns some internal buffer, e. g. `std::string`. With lvalue reference, your function might return a copy of. With rvalue reference, the function might assume you don't need that object any more anyway, and it might *move* the contents of the internal string to into the return value, avoiding the more expensive copy then. – Aconcagua Nov 29 '19 at 10:40
  • @Aconcagua i understand what means `const`, `&`, `&&`, qualified member function and i understand move semantics. What i don't understand is mixing `const` and `&`, `&&` as member function overload qualifiers. What exactly `const&` means? Like: "I allow to call this function using object...". What kind of object? I'm not refering to my specific example. Just in general what are usages of this qualifiers? – toozyfuzzy Nov 29 '19 at 11:19
  • Well, you can intermix, const-ness and l-/rvalue references are orthogonal principles. You can have all of these: l-value reference to mutable, l-value reference to const, r-value reference to mutable. Even r-value reference to const is possible, but what's the point of having move semantics if you cannot move from anyway??? So that's pretty meaningless... – Aconcagua Nov 29 '19 at 11:36
2

Const and reference member function qualifiers are to be able to apply those qualifier to "this" as for regular parameter, so mainly, you have something like:

int getVarLRef(SomeClass& self) { return self.val; }
int getVarCLRef(const SomeClass& self) { return self.val; }

And there, I think you know that:

getVarCLRef(SomeClass()); // Valid, temporary can bind to const lvalue reference
getVarLRef(SomeClass()); // INVALID, temporary CANNOT bind to non-const lvalue reference
Jarod42
  • 203,559
  • 14
  • 181
  • 302