93

Recently I was reading through the API of boost::optional and came across the lines:

T const& operator *() const& ;
T&       operator *() & ;
T&&      operator *() && ;

I also wrote my own program that defines member functions as const&, & and && (Note that I am not speaking about the return type, but the specifiers just before the semi-colons) and they seems to work fine.

I know what it means to declare a member function const, but can anyone explain what it means to declare it const&, & and &&.

lrineau
  • 6,036
  • 3
  • 34
  • 47
john_zac
  • 1,241
  • 2
  • 11
  • 16

2 Answers2

72

const& means that this overload will be used only for const, non-const and lvalue objects, such as:

const A a = A();
*a;

& means that this overload will be used only for non-const objects:

A a;
*a;

&& means that this overload will be used only for rvalue objects:

*A();

For more information about this feature of the C++11 standard you can read this post: What is "rvalue reference for *this"?

ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • 15
    If it helps, I like to think of these qualifiers as applying to the `*this` object that is "secretly" passed in as the first argument of methods. – Aaron McDaid Jan 21 '15 at 12:07
  • 4
    `const&` and `&` mean they will be used for a `const` and non-`const` *lvalue*. `&&` will be used only for an rvalue, not necessarily a reference. – Angew is no longer proud of SO Jan 21 '15 at 12:12
  • 3
    Actually, the `const &` can bind to pretty much anything, even an rvalue. Similar rules as apply to the binding of any parameter - `const T&` is kinda special - it can bind to anything that isn't `volatile` IIRC – Aaron McDaid Jan 21 '15 at 12:14
  • Thanks. Understood on a superficial level. Can you explain why this 3rd overload is necessary. I am guessing that they created this third overload so that a value can be moved out of the optional or any such structure while using * operator if the structure is created as an rvalue reference. But can't the compiler deduce automatically that the structure containing the member variable is an rvalue reference – john_zac Jan 21 '15 at 12:16
  • 3
    @john_zac You're right in why they did it, and no, a compiler cannot introduce `std::move`s as it sees fit. It could break the class's invariants, RAII semantics etc. The only place in the standard where something similar is allowed is `return`, where returning as an rvalue is tried first. – Angew is no longer proud of SO Jan 21 '15 at 12:20
  • @AaronMcDaid Wow, this never really dawned on me. – Angew is no longer proud of SO Jan 21 '15 at 12:20
  • 1
    @john_zac, these aren't quite "necessary", as you ask. But they provide certain optional optimizations. You could get away with just having `T operator *();` - this would compile but it would have some weird consequences - `*p = x` wouldn't do what you expect it to do. I think the important thing is the return type - we want multiple overloads with different return types. Once you understand the desirability of different return types, then you see also that we need the qualifiers on the end to control which overload (and therefore which return type) is used for each call. – Aaron McDaid Jan 21 '15 at 12:28
  • 1
    @Agnew. I think I understand. It is kind of analoguous to the case where we always need to explicity use 'std::move' to move member variables inside a move constructor – john_zac Jan 21 '15 at 12:29
  • 1
    Ever tried calling a `&`-member on a `const` object? It does not work. – Deduplicator Jan 21 '15 at 15:20
51

It is a member function ref-qualifiers; it is one of the features added in C++11. It is possible to overload non-static member functions based on whether the implicit this object parameter is an lvalue or an rvalue by specifying a function ref-qualifier (some details).

To specify a ref-qualifier for a non-static member function, you can either qualify the function with & or &&.

#include <iostream>
struct myStruct {
    void func() & { std::cout << "lvalue\n"; }
    void func() &&{ std::cout << "rvalue\n"; }
};
 
int main(){
    myStruct s;
    s.func();            // prints "lvalue"
    std::move(s).func(); // prints "rvalue"
    myStruct().func();   // prints "rvalue"
}
Alper
  • 12,860
  • 2
  • 31
  • 41