References in C++ 11

Surbhi Jain
2 min readAug 23, 2019

C++-11 References

Important Links:

http://thbecker.net/articles/rvalue_references/section_01.html

https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers

Cheat Sheet

Important Note:

l-value: For practical purposes, any expression whose address can be referenced is an l-value.

r-value: Any expression which is not an l-value.

X& x; // This is an lvalue-reference or normal reference and can bind only to l-values.

const X& x; // or equivalently X const& x is also an lvalue-reference can bind to both l-values and r-values.

C++11 Rvalue-references:

  • The goal is to provide a type which allows us to distinguish at compile time between normal (lvalue) references and references to rvalues. This allows us to specify more overloads when inputs are rvalues and hence can be mutated for efficiency purposes. The most common use case for these “efficiency purposes” is implementing copy constructors and assignments operators in a way that mutates the object being copied from, as it may be a temporary. This use case / overloading is called “move-semantics”.
  • “If-it-has-name”: If you have a named rvalue-reference and you further pass it to some function, it will be treated as:
  • An rvalue if it doesn’t have a name: For example:
    X&& foo();
    void bar(X&& x);
    bar(foo());
  • An lvalue if is has a name: For example:
    Derived::Derived(Derived&& d)://d is named, treated as lvalue when passed
    Base(std::move(d)) { // invokes Base(const Base&) without std::move
    }
  • Make sure that your overloads of move-constructor and move-assignment don’t throw exceptions and then make sure you annotate your overloads with the noexcept keyword. Otherwise STL containers like vectors may not always pick your move-semantics overloads.
  • std::move(): Casts its arguments into an rvalue. Note that this is just a cast, similar to static_cast<T&&>(t) where t is of type T.

C++11 Universal References:

  • && may not always mean rvalue-references. It may sometimes bind to either of lvalues or rvalues.
  • As a rule of thumb: T&& where T is a deduced type is a universal reference, otherwise it is an rvalue reference. This is most commonly applicable in situations where T is a template parameter, or with the use of ‘auto’, or even when using typedef and decltype.
  • C++-11 allows taking a reference to a reference with the following rules:
  • A& &, A& &&, A&& & are all same as A&
  • A&& && is A&&
  • template <typename T> void foo(T&& t):
  • If t is an lvalue then this becomes T&
  • It t is an rvalue then this becomes T&&
  • std::forward: Forward the same thing that was received at the call site, lvalue to lvalue and rvalue to rvalue. Note that if a value is named at the call site, then it will by default be forwarded as an lvalue otherwise.
    template <typename T> void foo(X&& x) { // can accept both l- and r- values
    bar(std::forward<X>(x)); // will forward l- as l- and r- as r-
    }

--

--

Surbhi Jain

Engineer @ Google | Technology Enthusiast | IIT Delhi | Computer Science