1

Is there a difference between l-value ref-qualified member functions and unqualified member functions? If so, what is it?

I.e., are these two ways of declaring func() different?

class Foo
{
  void func();
  void func() &;
};

I don't mean "are they different enough to permit overload resolution," because obviously the above class does not compile when both versions of func() are present. I mean, how and why does the compiler behave differently when & is included versus when it isn't?

There is at least one difference, which is that ref-qualified functions (of either l-value or r-value type) cannot be overloaded with non-ref-qualified functions. From the current standard draft:

Member function declarations with the same name and the same parameter-type-list as well as member function template declarations with the same name, the same parameter-type-list, and the same template parameter lists cannot be overloaded if any of them, but not all, have a ref-qualifier.

...but if this is the only difference, why the restriction? I.e., why can't foo()&& be treated as a valid override of (unqualified) foo()?

Kyle Strand
  • 15,941
  • 8
  • 72
  • 167

1 Answers1

3

They are different.

Foo foo;
std::move(foo).func();

will call func() but not func()&.

Similarly:

Foo make_foo() { return {}; }
make_foo().func();

will only work on the void func() signature, not the void func()& signature.

void func()& means that it only is a valid call on lvalues, not rvalues.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Oh, that makes sense. Is there any context in which `void func()&` is *less* restrictive? (I wouldn't expect so, but...) And what exactly is `return {};`? Does it just default-initialize a value of whatever the return-type is? I haven't seen that before. – Kyle Strand Jun 06 '15 at 00:51
  • @KyleStrand I am uncertain what you mean, but probably not. `return {};` is directly constructing the return value with zero arguments. – Yakk - Adam Nevraumont Jun 06 '15 at 00:53
  • Sorry, that's what I meant--not default-initialize. – Kyle Strand Jun 06 '15 at 00:54
  • @KyleStrand since C++11 a brace-enclosed list can be implicitly converted to a temporary – M.M Jun 06 '15 at 02:18
  • One good place to use lvalue ref is in the assignment operator: `T &operator=(T t) &` , this will prevent `bar() = 5;` for example, where `bar()` returns by value – M.M Jun 06 '15 at 02:19
  • @MattMcNabb in fact, most "my primary or only purpose is to mutate the object with no side effects" should be `&` qualified. Methods that return references to internal guts should have both `&` and `&&` with `&&` returning either a moved-copy, or a `&&` reference. – Yakk - Adam Nevraumont Jun 06 '15 at 02:22
  • @Yakk Yep, overloading a method to get a copy of some internal guts was exactly how I ran into this. – Kyle Strand Jun 06 '15 at 03:44