0xe3aad

0xe3aad

C++ Perfect Forwarding

Why Do We Need Perfect Forwarding#

Here is an example of a class factory function:

In the above example, the argument object arg is passed by value, which incurs the cost of creating additional temporary objects1. To avoid this, we can change it to pass by reference:

However, this implementation has a problem of not being able to bind to rvalue arguments. For example, factory<X>(42) will result in a compilation error. To further improve it, we can use const reference:

The problem with this implementation is that it does not support move semantics. By using rvalue reference for the function parameter, we can solve the problem of perfect forwarding.

Reference Collapsing#

Before C++11, we couldn't have a reference type refer to another reference type. However, with the introduction of rvalue references in C++, this practice has been relaxed2, resulting in the reference collapsing rule, which allows us to have references that can be both lvalue references and rvalue references. However, it follows the following rules:

Function Parameter TypeArgument TypeDeduced Function Parameter Type
T&lvalue referenceT&
T&rvalue referenceT&
T&&lvalue referenceT&
T&&rvalue referenceT&&

Template Argument Type Deduction#

For a function template template<typename T>void foo(T&&);, applying the reference collapsing rule mentioned above, we can conclude the following:

  • If the argument is an lvalue of type A, then the type of template parameter T is A&, and the type of the function parameter is A&.
  • If the argument is an rvalue of type A, then the type of template parameter T is A&&, and the type of the function parameter is A&&.

This also applies to type deduction in member function templates of class templates:

The function parameter of a function template must be in the form of T&& to require template argument type deduction. Even if the function parameter is declared as const T&&, it can only be used literally and does not require template argument type deduction.

Perfect Forwarding#

Here is the implementation of the perfect forwarding version of the above code:

Where std::forward is a template function defined in the standard library <utility>:

std::remove_reference is a class template defined in the standard library <type_traits>, used to remove the reference from a type. The type type defined in it is the underlying type of the reference.

  • When the data type of the argument is an lvalue reference type S& (T = S&), the type of t is S&, and static_cast<S& &&>(t) collapses to static_cast<S&>(t).
  • When the data type of the argument is an rvalue reference type S&& (T = S&&), the type of t is S&&, and static_cast<S&& &&>(t) collapses to static_cast<S&&>(t).

References#

https://en.wikipedia.org/wiki/Reference_collapsing

Footnotes#

  1. The lifetime of temporary objects is only within a single statement.

  2. The term relaxed is used here because references can only be indirectly defined as references in type aliases and template parameters.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.