Semantics moving and excellent forwarding

I already have the semantics of movements from this question: What is the semantics of movements?

But I still do not understand what ideal forwarding is associated with the semantics of movement.

Can someone explain in plain English and with a simple example what perfect redirects mean?

+7
c ++ c ++ 11 move-semantics perfect-forwarding
source share
2 answers

Normal attempt in English only

The problem is probably too complex to be accurately described using simple English sentences, but one could think of perfect forwarding as a way of transferring temporary values ​​passed to a function to another, as if the first function did not exist at all, therefore without either unnecessary copies or tasks. C ++ 11 allows you to do this by introducing some conversion rules between r-value (& &) and l-value (&) values ​​for the type when you try to get a link (either r-value or l-value) from them .

R-value references are a feature of C ++ 11, and they were developed for both movement semantics and perfect forwarding

This explanation is in plain English, but if you want to fully understand the problem, I would suggest the following:

Problem:

We want some temporary values ​​passed to the function F to be passed to another E without copying or assignment .

Attempts to solve this problem.

  • If you try to pass it by reference, for example

     template<typename T> void F(T& a) { E(a); } 

    you cannot use temporary (they are not l-values)

     F(1, 2, 3); // Won't work 
  • Declaring a link as const extends the lifetime of a temporary element on the stack (this was done historically to avoid a common frill link error), so the following works

     template<typename T> void E(const T& a) {} template<typename T> void F(const T& a) { E(a); } 

    but the disadvantage is that you will need to change the signature of the function (s) to fit this solution

  • If we are interested in the signature E (it must correspond to something), but not in F, we can leave with

     template<typename T> void E(T& a) {} template<typename T> void F(const T& a) { E(const_cast<T&>(a)); } 

    but in case it is called with a real constant and becomes unstable, it will cause undefined behavior

  • An invalid solution could be to identify all the options you need.

     template<typename T> void E(T& a) {} template<typename T> void F(T& a) { E(a); } template<typename T> void F(const T& a) { E(const_cast<T&>(a)); } 

    but as the number of parameters increases, the number of combinations grows: it is likely to become unattainable

Solution in C ++ 11

C ++ 11 defines some rules that state

"[given] type TR, which is a reference to type T, attempting to create type" lvalue reference to cv TR "creates type" lvalue reference to cv TR ", while trying to create type" rvalue reference to cv TR "creates type TR . "

in human form (TR = reference to type T, R = reference):

 TR R T& & -> T& // an lvalue reference to cv TR (becomes)-> lvalue reference to T T& && -> T& // an rvalue reference to cv TR (becomes)-> TR (lvalue reference to T) T&& & -> T& // an lvalue reference to cv TR (becomes)-> lvalue reference to T T&& && -> T&& // an rvalue reference to cv TR (becomes)-> TR (rvalue reference to T) 

The important conclusion here is that now you can track the type of function you receive: you can get the l-value and pass the same l-value to E, or you can get the r -value and pass the same r value (after its conversion, since the l-value reference to any type reference becomes the l-value reference) to E:

 template<typename T> void E(T&& a) {} template<typename T> void F(T&& a) { E(static_cast<T&&>(a)); } 

Syntactic sugar for

 static_cast<T&&>(a) 

is an

 std::forward<T>(a); // is the same as static_cast<T&&>(a); 

so the final code that solves the problem and makes your life easier

 template<typename T> void E(T&& a) {} template<typename T> void F(T&& a) { E(std::forward<T>(a)); } 

Living example


Links: Herb Sutter's blog and some other sources, which, unfortunately, I can no longer find. If someone has a key to these, write them in the comments below and I will update the message. Thanks.

+10
source share

Working with r-value references and link folding can be more complicated than at the beginning.

Excellent call forwarding

The ideal transfer is for the argument provided to the function to be forwarded (passed) to another function with the same category of values ​​(basically r-value vs l-value) as originally provided .

It is usually used with template functions where link folding could occur.

It can also be used within the same function .

Scott Meyers gives the following pseudo-code in his Going Native 2013 presentation to explain how std::forward works (approximately 20 minutes);
 template <typename T> T&& forward(T&& param) { // T&& here is formulated to disallow type deduction if (is_lvalue_reference<T>::value) { return param; // return type T&& collapses to T& in this case } else { return move(param); } } 

Example

Example from the site above, an archetypal example is the make_unique example

 template<class T, class... U> std::unique_ptr<T> make_unique(U&&... u) { return std::unique_ptr<T>(new T(std::forward<U>(u)...)); } 

In this example, the arguments for unique_ptr provided to him through make_unique , as if they were provided directly in unique_ptr , that is, the reference, l-value and r-value of the nature of the arguments are supported.

A more specific example:

 #include <iostream> #include <utility> #include <memory> struct A { // implementation excluded }; struct B { B(A &) // ctor 1 { std::cout << "ctor 1" << std::endl; } B(A&&) // ctor 2 { std::cout << "ctor 2" << std::endl; } }; int main() { A a; auto b1 = std::make_unique<B>(a); // ctor 1 is used auto b2 = std::make_unique<B>(A()); // ctor 2 is used } 

short info

Perfect forwarding depends on several fundamental language constructs new to C ++ 11, which form the basis of most of what we now see in general programming:

  • Link failed
  • Links to Rvalue
  • Move semantics

The use of std::forward is currently intended for the formula std::forward<T> , understanding how std::forward works, helps to understand why this is, and also helps to identify non-idiomatic or improper use of rvalues, reducing collapse links and so on .P..

Thomas Becker offers a nice but tight write about the perfect shipping problem and solution.

+2
source share

All Articles