C/C++ Flashcards
Right value Reference &&
2 main usages
1) Move semantics (Relies the argument is considered by compiler as T&&)
2) Perfect Forwarding (template ONLY) when T&& is lost, recover it so Move semantics can be invoked
Watch out the difference between
int &&
vs template
T &&
CONCRETE TYPE RVR:
if a variable is declared as concrete type int &&, then it can only accept R values (constant or temporary objects…i.e. things without a name)
TEMPLATE RVR
if a variable is declared as template type T && then it can accept either R values (constant or temporary objects…i.e. things without a name) or L values (anything with a name)
That is because for foo(T&& arg) the compiler will generate
foo(T & arg) to accept l-value and
foo(T && arg) to accept r-value
No name Rule – Applies to Right Value Reference (RVR) ONLY
Variables that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue.
T&& foo(T && arg) {
T arg2 = arg;
return arg2;
}
T arg3 = foo(T());
here incoming param is declared as RVR, but it has a name ‘arg’ this ‘arg’ is a l-value
here Return Value is declared as RVR, but “return value” has no name, thus the return value is a RVR, thus T(T&&) constructor will be invoked.
std: :forward(T &a) forward “reference” based on “reference collapsing rules”
std: :forward(T &a) GUARANTEE that the proper foo() is called:
foo(T &&) or foo(T) preserving move sematics OR
foo(T &) for l-value arguments
T&& forward(T& a) noexcept { return static_cast(a); }
based on type of invocation argument,
- T resolves to X, then return X&&
- T resolves to X&, then return X& && –> X&
- T resolves to X&&, then return X&& && –> X&&
so we can see r-value or RVR is forwarded as RVR
l-value is forwarded as l-value
Perfect Forwarding == T&& arg + std::forward(arg)
void perfect_forwarding_example(T && arg) { /* inside function, arg is now a l-value because it has a name */ /* this recovers the original L/R value property of arg */ std::forward(t) }
In function argument declaration,
use T&& if you want to accept either L-value or R-value or RVR
Inside function, use std::forward(arg) if arg is declared as T&& and you want to recover its “move semantics”
That is exactly what std::move does
1) declare T&& as argument so it accepts L or R
2) inside use std::forward() to recover RVR
template typename remove_reference::type&& std::move(T&& a) noexcept { /* this two lines does same as std::forward() typedef typename remove_reference::type&& RvalRef; return static_cast(a); }