C++11: Rvalue references for *this

Contents

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:

structfoo{voidbar();// [1]
voidbar()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.)

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.

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.)

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:

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

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

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:

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:

fooconstf3();// function returning a const foo
barconstf4();intmain(){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:

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.