Perfect Forwarding

Universal References? Forwarding References? REALLY?

This is a good ol’ function that takes an rvalue reference (to a std::string for that matter):

void function(std::string&& s) { ... }

This is a function template, parameterized with type T, that takes a universal reference - a.k.a. forwarding reference:

template <typename T>
void function(T&& param) { ... }

Universal Reference? Binds To Lvalues?

RValue references

  • A function taking an rvalue reference cannot take an lvalue

  • Reason: if it did, it could do harm to something the caller might still want to use

  • See Move Semantics, Rvalue References

#include <string>

void function(std::string&& rvref)
{
}

int main()
{
    std::string lvalue;
    function(lvalue);
    return 0;
}
c++11-rvalue-reference.cpp:10:14: error: cannot bind rvalue reference of type ‘std::string&&’ {aka ‘std::__cxx11::basic_string<char>&&’} to lvalue of type ‘std::string’ {aka ‘std::__cxx11::basic_string<char>’}
   10 |     function(lvalue);
      |              ^~~~~~
c++11-rvalue-reference.cpp:3:29: note:   initializing argument 1 of ‘void function(std::string&&)’
    3 | void function(std::string&& rvref)
      |               ~~~~~~~~~~~~~~^~~~~

But: function templates share the same syntax to mean something completely different ⟶ UNIVERSAL REFERENCES

#include <iostream>

template<typename T>
void function(T&& t)
{
    std::cout << "  accepted" << std::endl;
}

int main()
{
    std::cout << "passing lvalue" << std::endl;
    std::string lvalue;
    function(lvalue);

    std::cout << "passing rvalue reference" << std::endl;
    function(std::move(lvalue));
    return 0;
}
$ ./c++11-universal-reference
passing lvalue
  accepted
passing rvalue reference
  accepted

Enter Forwarding

  • Universal references accept many variations (sigh)

  • ⟶ how can those be differentiated? Forwarded to other functions?

#include <iostream>
#include <string>

void lo_function(const std::string&)
{
    std::cout << "  lo_function(const std::string&)" << std::endl;
}

void lo_function(std::string&&)
{
    std::cout << "  lo_function(std::string&&)" << std::endl;
}

template <typename T>
void function(T&& t)
{
    lo_function(std::forward<T>(t));
}

int main()
{
    std::cout << "passing lvalue" << std::endl;
    std::string lvalue;
    function(lvalue);

    std::cout << "passing rvalue reference" << std::endl;
    function(std::move(lvalue));
    return 0;
}
$ ./c++11-universal-reference-forward
passing lvalue
  lo_function(const std::string&)
passing rvalue reference
  lo_function(std::string&&)

Exercise: Dual insert() Method In Exercise: Bag Of Items, By Shared Reference

Modify the implementation of Exercise: Bag Of Items, By Shared Reference to implement two insert() methods with just one template method:

  • insert(const std::shared_ptr<Item>&)

  • insert(std::shared_ptr<Item>&&)

Further Information