Hot C++11, Part 3: Perfect Forwarding And Universal References
Upcoming SlideShare
Loading in...5
×
 

Hot C++11, Part 3: Perfect Forwarding And Universal References

on

  • 517 views

The slides for my mini-lecture "Hot C++11: Perfect Forwarding And Universal References" that I've done for my colleagues in the company I'm working at. Note that the slides without the lecture itself ...

The slides for my mini-lecture "Hot C++11: Perfect Forwarding And Universal References" that I've done for my colleagues in the company I'm working at. Note that the slides without the lecture itself can be not clear enough.

Statistics

Views

Total Views
517
Views on SlideShare
515
Embed Views
2

Actions

Likes
0
Downloads
8
Comments
0

2 Embeds 2

https://twitter.com 1
http://www.slideee.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Hot C++11, Part 3: Perfect Forwarding And Universal References Hot C++11, Part 3: Perfect Forwarding And Universal References Presentation Transcript

    • Andrey Upadyshev Hot C++11:
 Perfect Forwarding and Universal References Licensed under a CC BY-SA 4.0 License. Version of 2014.03.05
    • Perfect Forwarding: The Problem Simple factory:! template<typename T, typename Arg> shared_ptr<T> factory(Arg arg) { return shared_ptr<T>(new T(arg)); } The intent here is to forward the argument arg from the factory function to T's constructor.! Ideally, everything should behave just as if the factory function weren't there and the constructor were called directly in the client code: perfect forwarding. ! Doesn't work if constructor takes arg by reference. "2
    • C++98 Solution Take argument by reference:! template<typename T, typename Arg> shared_ptr<T> factory(Arg& arg) { return shared_ptr<T>(new T(arg)); } Then failed with rvalues:! factory<X>(foo()); // foo returns by value factory<X>(41); Overload must be provided:! template<typename T, typename Arg> shared_ptr<T> factory(const Arg& arg) { return shared_ptr<T>(new T(arg)); } "3
    • C++98 Solution Is Far From Perfect template<typename T, typename Arg> shared_ptr<T> factory(Arg& arg); template<typename T, typename Arg> shared_ptr<T> factory(const Arg& arg); ! Drawbacks:! 1. 2 ^ number_of_args overloads for general solution! 2. Move semantics blocked "4
    • Way To Universal References 1. C++11 has new reference collapsing rules:! • T&& && collapses into T&& • All other references to references (T& &, T&& &, T& &&) collapses into T&! 2. C++11 has special template argument deduction rule for rvalue reference to a template argument:! template<typename T> void foo(T&& t);! • • When called with lvalue of type A then T resolves to A& → the argument type becomes A& When called with rvalue of type A then T resolves to A → the argument type becomes A&& "5
    • Universal References If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.! — Scott Meyers, Universal References in C++11 ! template<typename T> void foo(T&& t); // t is universal reference "6
    • Universal References: Examples template<typename T> void f(T&& param); // deduced parameter type ⇒ type deduction; // && ≡ universal reference template<typename T> class Widget { Widget(Widget&& rhs); // fully specified parameter type ⇒ no type deduction; ... // && ≡ rvalue reference }; template<typename T1> class Gadget { template<typename T2> Gadget(T2&& rhs); // deduced parameter type ⇒ type deduction; ... // && ≡ universal reference }; void f(Widget&& param); // fully specified parameter type ⇒ no type deduction; // && ≡ rvalue reference "7
    • Perfect Forwarding Universal reference is a key:! template<typename T, typename Arg> shared_ptr<T> factory(Arg&& arg) { return shared_ptr<T>( new T(std::forward<Arg>(arg))); } No overloads needed!! Note that std::forward forwards the argument to another function exactly as it was passed to the calling function. "8
    • Even Better With Variadic Templates template<typename T, typename A1, typename A2> shared_ptr<T> factory(A1&& a1, A2&& a2) { return shared_ptr<T>( new T(std::forward<A1>(a1), std::forward<A2>(a2))); } ! template<typename T, typename... Args> shared_ptr<T> factory(Args&&... args) { return shared_ptr<T>( new T(std::forward<Args>(args)...)); } "9
    • The Caveats: Avoid Overloading on URefs template<typename T> void foo(T const& t) {…} template<typename T> void foo(T&& t) {…} Idea was: all lvalues go to the first, all rvalues go to the second.! Reality bites: const lvalues go to the first, all the rest go to the second (the second takes universal reference) => accidental move from lvalue is possible. "10
    • The Caveats: Avoid Overloading on URefs Simple solution is to use compile time dispatching:! template<typename T> void foo_impl(T&& t, std::true_type) {…} // lvalues ! template<typename T> void foo_impl(T&& t, std::false_type) {…} // rvalues ! template<typename T> void foo(T&& t) { foo_impl(std::forward<T>(t), std::is_lvalue_reference<T>()); } "11
    • The Caveats: URefs And Copy Constructor Consider the simple wrapper:! template<typename T> struct Wrapper { T m_value; ! }; template<typename U> Wrapper(U && u): m_value(std::forward<U>(u)) {} The array is perfectly forwarded to the string constructor:! Wrapper<std::string> str(“Hello, World!”); "12
    • The Caveats: URefs And Copy Constructor template<typename T> struct Wrapper { T m_value; ! }; template<typename U> Wrapper(U && u): m_value(std::forward<U>(u)) {} Looks OK? Sadly not. In the following case, the compiler uses the above constructor as a copy constructor:! Wrapper<std::string> s1(“Hello, World!”); Wrapper<std::string> s2 = s1; "13
    • The Caveats: URefs And Copy Constructor template<typename T> struct Wrapper { T m_value; ! ! template<typename U> Wrapper(U && u): m_value(std::forward<U>(u)) {} // Compiler generated copy constructor: Wrapper(wrapper const& op): m_value(op.m_value) {} }; Wrapper<std::string> s1(“Hello, World!”); Wrapper<std::string> s2 = s1; First constructor is a better match because U&& can be deduced as wrapper<std::string>&! Although a template is never used to generate a copy constructor, a template may end up being used anyway if overload resolution selects it. "14
    • The Caveats: URefs And Copy Constructor Note that variadic template constructors has the same problem:! template<typename... Ts> struct tuple { // Can be used instead of a copy constructor }; template<typename... Us> tuple(Us&&... us); Note also that assignment operator smells the same. "15
    • The Caveats: URefs And Copy Constructor Possible solution is to disable forwarding constructor for types derived from Wrapper:! template<typename Base, typename Derived> using disable_if_same_or_derived_t = typename std::enable_if< !std::is_base_of< Base, typename std::remove_reference<Derived>::type >::value >::type; ! template<typename T> struct Wrapper { T m_value; ! }; template<typename U, typename Disable = disable_if_same_or_derived_t<wrapper, U>> Wrapper(U && u): m_value(std::forward<U>(u)) {} "16
    • Useful Links Thomas Becker, Perfect Forwarding: The Problem
 http://thbecker.net/articles/rvalue_references/section_07.html! Scott Meyers, Universal References
 http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers! Eric Niebler, Universal References and the Copy Constructor
 http://ericniebler.com/2013/08/07/universal-references-and-the-copy-constructo/! (Im)perfect forwarding with variadic templates
 stackoverflow.com/questions/13296461/imperfect-forwarding-with-variadictemplates! C++11 Standard (final plus minor editorial changes)! http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf "17