Recently, gcc added support rvalue references for *this. (Clang has supported it for quite a while now.) In this post, I show how to use this feature, and how it means we can finally define accessors and a few other things like operator= correctly.

How it used to be

Before C++11, four different versions of non-static member functions could be declared: non-const, const, volatile and const volatile. (Though the volatile versions are barely ever used.)

For example:

struct foo {
    void bar(); // [1]
    void bar() const; // [2]
};

Overload [1] can only be called on non-const foo objects. However, [2] can be called on both const and non-const foo objects. (But if [1] exists, it will be used instead of [2] for non-const objects.)

For example:

foo f(); // function that returns a foo object
foo a;
foo const & b = a;

int main() {
    a.bar(); // calls [1]
    b.bar(); // calls [2]
    f().bar(); // calls [1]
}

Okay, but what’s new?

Since C++11, there are an additional eight versions of a non-static member function that can be declared: The original four with either & or && added (for example, const &, or &&). If any of the versions you declare are of this new eight, all of them must be.

Let’s take this simple example:

struct foo {
    void bar() &; // [1]
    void bar() &&; // [2]
    void bar() const &; // [3]
};

Here, [1] can only be called on objects which can be bound to a foo &. Similarly, [2] and [3] can only be called on objects which you can use as foo && or foo const &, respectively. This means that [1] can not be used for rvalue foo objects, and similarly [2] can not be used for lvalues. Since a foo const & can bind to both const and non-const lvalues and rvalues, it will be used for any (non volatile) object that does not match another overload. (So in this case, with both [1] and [2] declared, it will only be used for const objects.)

For example:

foo f(); // function that returns a foo object
foo a;
foo const & b = a;

int main() {
    a.bar(); // calls [1]
    b.bar(); // calls [3]
    f().bar(); // calls [2]
}

Why would I want that?

More often than not, a non-const non-static member function only makes sense for a lvalue. For instance, it wouldn’t make much sense to call the .clear() on a rvalue std::string:

std::string get_name();
get_name().clear(); // This is allowed, but it doesn't make much sense.

If std::string::clear were declared as &, this code would not be allowed.

There are quite a few cases in which this feature will come in handy. What follows is a few examples of such cases.

Daisy Chaining

Daisy chaining is a commonly used pattern, which allows you to call multiple member functions in a row:

struct foo {
    foo & do_this() { /*...*/ return *this; }
    foo & do_that() { /*...*/ return *this; }
    foo & and_that() { /*...*/ return *this; }
};

int main() {
    foo f;
    f.do_this().do_that().and_that();
}

However, this pattern doesn’t work very well for rvalue foo objects:

foo f();
int main() {
    f(); // A rvalue
    f().do_this().do_that(); // [1] A rvalue or lvalue (reference)?
}

Here, [1] results in an lvalue reference, simply because do_that() is declared to return a lvalue reference. (So it can turn a rvalue into a lvalue reference! Even static_cast can’t do that.)

Although this is not a disaster, it can be slightly inconvenient:

int main() {
    foo a = f(); // moves
    foo b = f().do_this().do_that(); // copies
}

The example can be corrected to work properly for rvalues as follows:

struct foo {
    foo & do_this() & { /*...*/ return *this; }
    foo && do_this() && {
        do_this(); // [1]
        return std::move(*this);
    }
    // ...
};

Here, the first overload is only called on lvalues, and returns an lvalue reference. The second overload is called for rvalues, and thus returns a rvalue reference.

An interesting thing here is that [1] calls the first (&) overload. Although a bit counterintuitive, inside && member functions, *this is still a lvalue (just like x is an lvalue inside void f(X && x)).

Operator =

Rvalues are called ‘r’values because in an assignment expression (a = b) they should appear on the right hand side. As you would expect, this does not compile:

int get_age();

int main() {
    get_age() = 5; // Error: lvalue required as left operand of assignment
}

It might surprise you, however, that the following example does compile:

std::string get_name();

struct foo { /*...*/ };
foo f();

int main() {
    get_name() = "abc"; // No error

    foo a;
    f() = a; // No error
}

(And even more surprising: Neither gcc nor clang give a warning here.)

The reason this code is allowed is that operator = for both foo and std::string is simply a non-const (pre C++11 style) member function, and thus accepts any non-const (and non-volatile) object as *this.

For foo, this can be fixed by declaring (the default) operator = only for lvalues:

struct foo {
    foo & operator = (foo const &) & = default;
    foo & operator = (foo &&) & = default;
};

int main() {
    foo a;
    f() = a; // Error: no viable overloaded '='
}

There are, as far as I know, no plans to change the default operator= to a &-member or to change any of the existing member functions of the standard containers to use this new feature.

Note that the same as for operator= holds for other modifying operators:

int main() {
    std::list<int> l;
    l.size()++; // Error (.size() gives a fundamental type)
    l.begin()++; // OK (.begin() doesn't)
}

Accessors

An accessor is usually a trivial member function that gives you access to a member variable:

class foo {
public:
    std::string name;
};

class bar {
    std::string name_; // Not public, for some reason.
public:
    std::string & name() { return name_; }
    std::string const & name() const { return name_; }
};

foo a;
bar b;

Now, b.name() can be used just like a.name can: a.name = "x", b.name() = "x", etc. As you might expect, however, it does not behave the same with respect to rvalues:

foo f1();
bar f2();

int main() {
    f1().name; // [1] This is an rvalue (it is a member of the rvalue f1()).
    f2().name(); // [2] Is this also an rvalue?
}

No, because name() is declared to return a lvalue reference, [2] is an lvalue, unlike [1]. So,

std::string x = f1().name; // Moves
std::string y = f2().name(); // Copies

Solution:

class bar {
    std::string name_;
public:
    std::string & name() & { return name_; }
    std::string && name() && { return std::move(name_); }
    std::string const & name() const & { return name_; }
};

int main() {
    f1().name; // An rvalue
    f2().name(); // An rvalue, too
    std::string x = f1().name; // Moves
    std::string y = f2().name(); // Moves, too
}

(No, there isn’t a way to declare or define all three accessors at once, unfortunately.)

Other accessors, like operator[], should also take rvalues into account:

std::vector<std::string> v();
std::string x = v()[0]; // Copies, doesn't move.

Here, the string is copied, because operator[] returns a std::string &, as it doesn’t have a special overload for && to return a std::string &&.

What about the other one/five?

I mentioned there are eight new ways to overload a member function on the type of *this, but all the examples so far only used three: &, &&, and const &. What about the other ones?

Well, four of those are for volatile objects, which are barely ever used. If you’ve never made a volatile member function before, it’s likely you’re never going to use it.

So, ignoring all the volatile ones, one remains: const &&. I ignored it as well in the examples, because it is a rather useless thing. It’s an rvalue reference, but you can’t move from it, since it is const. This means that in almost any case, you’ll handle a const && just like you would handle a const &, to which any rvalue can bind as well.

Ignoring it made the last example not completely correct, however:

foo const f3(); // function returning a const foo
bar const f4();

int main() {
    f3().name; // This is a const &&
    f4().name(); // This is a const &
}

So if you want a.name and b.name() to have the exact same behaviour, you should also define a const && version. (And while you’re busy, you might as well make the volatile overloads, too.) But like I said, since you can’t move from a const && anyway, it doesn’t really matter if you get a const & instead.

So, defining accessors has gotten a lot more annoying, as we now have to define three/four/eight instead of two/four of them.

I hope that some day, there will be a syntax to support defining all those eight overloads of a member function at once. Maybe something like:

class foo {
    std::string name_;
public:
    decltype(auto) name() T && { return std::forward<T>(*this).name_; }
}:

Conclusion

Try not to use ‘old style’ non-const (non-static) member functions (void foo()) anymore, since those can implicitly bind a rvalue to a lvalue reference. Most of the time, when you declare a member function void foo(), you should’ve used void foo() & instead, as most modifying member functions don’t make sense for rvalues. The same holds for member functions like operator = and operator ++.

For const it doesn’t really matter, as any const (non-volatile) object can bind to a const & anyway. It’s just that if you have void foo() &, the const version must also be declared as a reference: void foo() const &.

In the cases that it does make sense to use a non-const member function on a rvalue, such as in the case of an non-const accessor, or when daisy chaining, also make a && version.

When there’s no difference between copying and moving your object (for instance, when it is POD), it doesn’t really matter whether you get a T&& or a T&. So when that’s the case, and it does make sense to use a non-const member function on rvalues, you could still use an old style non-const member function to save some typing.