Sumant Tambe, Ph.D.
Senior Software Research Engineer
Real-Time Innovations, Inc.


http://cpptruths.blogspot.com
Blogger


          Library Developer


Author
This talk is about programming language idioms

    Definitions taken from Answers.com and Google.com
» Idioms
  ˃ Specific grammatical, syntactic, structural
    expressions in a given programming language
    to solve an implementation problem
  ˃ Often have names—create vocabulary like
    patterns
  ˃ Repeat themselves
  ˃ Often cannot be modularized as libraries
  ˃ May get promoted as first-class features as a
    language evolves

» Idioms vs. Patterns
  ˃ Idioms are patterns specific to a language
  ˃ Patterns are language independent
» Idioms of Using C++11 Rvalue References

» C++ Truths
   ˃ Rvalue References In Constructor: When Less Is More
   ˃ Perfect Forwarding of Parameter Groups in C++11


» A Sense of Design – Boris Kolpackov
   ˃ Efficient Argument Passing in C++11 (Parts 1, 2, and 3) (with permission)
class Book {
// ....

private:
   std::string                _title;
   std::vector<std::string>   _authors;
   std::string                _publisher;
   size_t                     _pub_year;
};
class Book {
public:
  Book(const   std::string &              title,
       const   std::vector<std::string> & authors,     Good old C++03
       const   std::string &              pub,         constructor
       const   size_t &                   pub_year);
// ....

private:
  std::string                _title;
  std::vector<std::string>   _authors;
  std::string                _publisher;
  size_t                     _pub_year;
};


 » Other alternatives
    ˃ An iterator-pair instead of const std::vector<std::string>&
    ˃ but lets keep it simple
class Book {
public:
  Book(const   std::string &              title,
       const   std::vector<std::string> & authors,        Constructor
       const   std::string &              pub,            parameters
       const   size_t &                   pub_year)

   : _title(title),
     _authors(authors),
     _publisher(pub),              make copies in *this
     _pub_year(pub_year)
    {}

private:
  std::string                _title;
  std::vector<std::string>   _authors;
  std::string                _publisher;
  size_t                     _pub_year;
};


                       Is this class move enabled?
class Book {
public:
  Book(const   std::string &              title,
       const   std::vector<std::string> & authors,         No need to
       const   std::string &              pub,             write these
       const   size_t &                   pub_year);       declarations

 Book (const Book &)              = default; //   Copy Constructor
 Book & operator = (const Book &) = default; //   Copy Assign
 Book (Book &&)                   = default; //   Move Constructor
 Book & operator = (Book &&)      = default; //   Move Assign
 ~Book()                          = default; //   Destructor

private:
  std::string                _title;                     Compiler will
  std::vector<std::string>   _authors;                   provide them
  std::string                _publisher;                    where
  size_t                     _pub_year;                   appropriate
};
But, I Just READ About
 Rvalue References!
class Book {
public:
  Book(const   std::string &              title,
       const   std::vector<std::string> & authors,
       const   std::string &              pub,
       const   size_t &                   pub_year); // Old c-tor

 Book(std::string &&              title,
      std::vector<std::string> && authors,
      std::string &&              pub,
      size_t &&                   pub_year)        Constructor with C++11
    : _title(std::move(title)),                    rvalue references
      _authors(std::move(authors)),
      _publisher(std::move(publisher),
      _pub_year(std::move(pub_year))
    {}

// ... Members not shown
};                              Is it now optimally move enabled?
class Book {
public:
  Book(const    std::string &              title,
        const   std::vector<std::string> & authors,
        const   std::string &              pub,
        const   size_t &                   pub_year); // Old constructor

  Book(std::string &&               title,
        std::vector<std::string> && authors,
        std::string &&              pub,
        size_t &&                   pub_year) // New constructor
     : _title(std::move(title)),
        _authors(std::move(authors)),
        _publisher(std::move(publisher),
        _pub_year(std::move(pub_year))
     {}
};

int main(void)
{
  std::vector<std::string> authors { "A", "B", "C" };           Calls old
  Book b1("Book1", authors, "O’Reilly", 2012);                constructor!

    const size_t year = 2012;                                   Calls old
    Book b2("Book1", { "Author" }, "O’Reilly", year);
                                                              constructor!
    Book b3("Book", { "Author" }, "O’Reilly", 2012); // Calls New Ctor
}
Book::Book(std::string &&              title,
           std::vector<std::string> && authors,
           std::string &&              pub,
           size_t &&                   pub_year); // New constructor

                                     lvalue
int main(void)
{
  std::vector<std::string> authors { "A", "B", "C" };
  Book b1("Book1", authors, "O’Reilly", 2012);      lvalue

    const size_t year = 2012;
    Book b2("Book1", { "Author" }, "O’Reilly", year);
                                                             No lvalue
    Book b3("Book", { "Author" }, "O’Reilly", 2012);
}

» Even one incompatible parameter (lvalue) will cause the
  compiler to reject the new rvalue-only constructor
» Fortunately an optimal solution exists!
» But has a small problem  8 constructors!
    ˃ Implementations of all the constructors are different!
    ˃ Each Rvalue reference object must be std::moved
    ˃ In general exponential number of constructors
      title              authors                pub              year
string &&        vector<string> &&       string &&             size_t
string &&        vector<string> &&       const string &        size_t
string &&        const vector<string> & string &&              size_t
string &&        const vector<string> & const string &         size_t
const string &   vector<string> &&       string &&             size_t
const string &   vector<string> &&       const string &        size_t
const string &   const vector<string> & string &&              size_t
const string &   const vector<string> & const string &         size_t
» Help the compiler take the best decision
   ˃   Pass-by-value!
   ˃   std::move each parameter
   ˃   Compiler makes no more copies than absolutely necessary
   ˃   Copy-elision may avoid moves too!

class Book {
public:
  Book(std::string              title,
       std::vector<std::string> authors,
       std::string              pub,
       size_t                   pub_year)
                                                  Only one constructor
    : _title    (std::move(title)),
      _authors (std::move(authors)),
      _publisher(std::move(pub)),
      _pub_year (std::move(pub_year))
     {}
};
class Book {
public:
  Book(std::string              title,
       std::vector<std::string> authors,
       std::string              publisher,
       size_t                   pub_year)
    : _title    (std::move(title)),
      _authors (std::move(authors)),
      _publisher(std::move(publisher)),
      _pub_year (pub_year)
     {}
};

std::string publisher() { // ... }
                                     lvalue
int main(void) {
  std::vector<std::string> authors { "A", "B", "C" };
  Book b1("Book1",   authors,   publisher(),   2012);
}


          ctor +   Copy-ctor +   2 moves      2 copies
         1 move      1 move
» Move is not free!
   ˃ Potentially 5 to 15 % performance degradation
   ˃ Benchmark!
» Many types don’t have efficient move operations
   ˃ std::array, std::complex, etc.
   ˃ Many C++03 libraries have not caught up with C++11
      + Compiler will not provide implicit move operations (in most cases)
» Move may be same as copy!
   ˃ Move construction  Use move-ctor if available, otherwise use copy-
     ctor!!
   ˃ Move==Copy when strings are small (small string optimization)
» Works best only when you know you are going to make a
  copy
A sense of design
matrix operator+ (const matrix& x, const matrix& y)
{
  matrix r (x);
  r += y;
  return r;
}


» matrix is movable (efficiently)
» matrix is copyable too
» operator + makes a copy
   ˃ Lets try to save it if we can!
matrix operator+ (matrix&& x, const matrix& y)
{
  matrix r (std::move(x));
  r += y;                              Look ma!
  return r;                            No Copy!
}

matrix a, b;
matrix c = a * 2 + b;
                                      Works great!


matrix d = a + b * 2;

                                      Oops!
matrix operator+ (const matrix& x, const matrix& y) {
  matrix r (x);
  r += y;                                           ARE YOU
  return r;                                         KIDDING
}                                                     ME?
matrix operator+ (const matrix& x, matrix&& y) {
  matrix r (std::move(y));
  r += x;
  return r;
}
matrix operator+ (matrix&& x, const matrix& y) {
  matrix r (std::move(x));
  r += y;
  return r;
}
matrix operator+ (matrix&& x, matrix&& y) {
  matrix r (std::move(x));
  r += y;
  return r;
}
            In general, exponential number of implementations
» Pass-by-value!
matrix operator+ (matrix x, matrix y) {
  matrix r (std::move(x));
  r += y;
  return r;
}

matrix a, b;
matrix c = a*2 + b*4;                     Works great!


matrix d = a + b*4;
                           Two Unnecessary Moves
matrix e = a*2 + b;

matrix f = a + b;                    One Unnecessary Copy and Move


          We need a way to detect lvalue/rvalue at runtime
struct X { ... };
                             » Pass original type to a
void g(X&& t); // A            forwarded function
void g(X& t); // B              ˃ Forward lvalues as lvalues
template<typename T>            ˃ Forward const lvalues as const lvalues
void f(T&& t)                   ˃ Forward rvalues as rvalues
{
    g(std::forward<T>(t));   » Works very well with factory
}                              functions
int main()                   template <class Arg>
{                            Blob * createBlob(Arg&& arg)
    X x;                     {
    f(x);   // Calls B        return new Blob(std::forward<Arg>(arg));
    f(X()); // Calls A       }
}
»   Perfect forwarding can be made to work … But
»   First, you must use a template
»   Second, you need two template parameters (T and U)
»   Then you must restrict them to the specific type you
    are interested in (i.e., matrix)
     ˃ Most likely using enable_if
» Finally, you need to ask the right question
     ˃ IS_RVALUE(T, x)?
    #define IS_RVALUE(TYPE, VAR) 
    (std::is_rvalue_reference<decltype(std::forward<TYPE>((VAR)))>::value)
    (!std::is_reference<TYPE>::value)


                      In short, It is too much noise!
Parameter          Example               Best Case          Sub-optimal Case
Const        f(const matix &);        Function makes     Function makes a copy and
Reference                             no copy and pass   pass rvalue
                                      lvalue             (missed optimization)
Pass-by-value f(matrix);              Function makes a   Function makes no copy
                                      copy and pass      and pass lvalue
                                      rvalue             (unnecessary copy)
Const        f(const matrix &);       All cases          For N parameters, 2N
Reference    AND                                         implementations
AND          f(matrix &&);
Rvalue
Reference
Perfect      template <class     T>   When you need a When you don’t want a
Forwarding   f(T&&);                  template        template (complex use of
(T &&)                                                enable_if)


                       Can we do better?
» A type that
   1.   Binds to lvalues as const reference
   2.   Binds to rvalues as rvalue reference
   3.   Determines lvalue/rvalue at run-time
   4.   Does not force the use of templates
   5.   Causes no code bloat
   6.   Is built-in

» Const reference does not satisfy #3
» Perfect forwarding does not satisfy #4
» std::reference_wrapper does not satisfy #2 and #3
template <typename T> struct in {
  in (const T& l): v_ (l), rv_ (false) {}
  in (T&& r)     : v_ (r), rv_ (true) {}

 bool lvalue () const { return !rv_; }
 bool rvalue () const { return rv_; }

 operator const T& () const { return v_; }
 const T& get () const { return v_; }
 T&& rget () const { return std::move (const_cast<T&> (v_)); }

  T move () const {
    if (rv_)
      return rget ();
    else
      return v_;
  }
private:
  const T& v_;
  bool rv_;
};
matrix operator + (in<matrix> m1, in<matrix> m2)
{
  if(m1.rvalue()) {
    matrix r(m1.rget());
    r += m2.get();
    return r;
  }                               » No template!
  else if(m2.rvalue()) {
    matrix r(m2.rget());
                                  » Simple, self-explanatory
    r += m1.get();
    return r;
                                  » Can be even shorter!
  }
  else {
    matrix r(m1.get());
    r += m2.get();
    return r;
  }
}
» Idiom useful only when copies of parameters are made
  conditionally
   ˃ See authors blog for more details
» Not clean when ambiguous implicit conversions are
  involved
   ˃ See authors blog for more details
» Not well-known yet
   ˃ May be surprising to some
» More critical eyes needed
   ˃ Propose in Boost?
» Should be built-in, ideally
   ˃ Propose a language extension for C++1y?
class Blob {
private:
  std::vector<std::string> _v;
};

» Initialize Blob  Initializing Blob::_v
» C++11 std::vector has 9 constructors!
» Question: How to write Blob’s constructor(s) so that
  all the vector’s constructors could be used?
int main(void)
{
  const char * shapes[3] = { "Circle", "Triangle", "Square" };

    Blob b1(5, "C++ Truths"); // Initialize Blob::_v with 5 strings
    Blob b2(shapes, shapes+3); // Initialize Blob::_v with 3 shapes
}
class Blob {
  std::vector<std::string> _v;

public:
  template<class Arg1, class Arg2>
  Blob(Arg1&& arg1, Arg2&& arg2)
                                         Perfect forward
   : _v(std::forward<Arg1>(arg1),
                                         two parameters
        std::forward<Arg2>(arg2))
  { }
};

int main(void)
{
  const char * shapes[3] = { "Circle", "Triangle", "Square" };

    Blob b1(5, "C++ Truths"); // OK
    Blob b2(shapes, shapes+3); // OK
}
class Blob {
  std::vector<std::string> _v;
public:
  template<class Arg1, class Arg2>
  Blob(Arg1&& arg1, Arg2&& arg2)
   : _v(std::forward<Arg1>(arg1),        Perfect forward
        std::forward<Arg2>(arg2))        two parameters
  { }
};

int main(void)
{
   Blob b3;      // Default constructor. Does not compile!
   Blob b4(256); // 256 empty strings.   Does not compile!
}
class Blob {
  std::vector<std::string> _v;
public:
  template<class... Args>
  Blob(Args&&... args)
   : _v(std::forward<Args>(args)...)
  { }
};

int main(void)
{
  const char * shapes[3] = { "Circle", "Triangle", "Square" };

    Blob   b1(5, "C++ Truths");    //   OK
    Blob   b2(shapes, shapes+3);   //   OK
    Blob   b3;                     //   OK
    Blob   b4(256);                //   OK
}
class Blob {
  std::vector<std::string> _v;
  std::list<double> _l
public:
  template<class... Args>
  Blob(Args&&... args)
   : _v(???),                What parameters
     _l(???)                    are for _v and
  { }                          what are for _l?
};

int main(void)
{
 // Initialize Blob::_v with 5 strings and
 // Blob::_l with { 99.99, 99.99, 99.99 }
 Blob b1(5, "C++ Truths", 3, 99.99); // Does not compile!
}
int main(void)
{
  Blob b1(5, "C++ Truths", 3, 99.99);
}


int main(void)
{
  Blob b1(std::make_tuple(5, "C++ Truths"),
          std::make_tuple(3, 99.99));
}


» But std::vector and std::list don’t accept
  std::tuple as a parameter to constructor
» Packing parameters into a tuple is easy
    ˃ Just call std::make_tuple
» Unpacking is easy too
    ˃ Use std::get<N>
int main(void)
{
  std::tuple<int, double> t =    std::make_tuple(3, 99.99);

    std::list<int> list (std::get<0>(t), std::get<1>(t));
}

» But the number and types of parameters may vary
» We need a generic way to unpack tuples
     ˃ This is extraordinarily complicated
» If you have a tuple
t = (v1, v2, v3, …, vN)


» You need
get<0>(t), get<1>(t), get<2>(t), …, get<N-1>(t)



                   At Compile Time!
» If you have a tuple
t = (v1, v2, v3, …, vN)


» And if you have another type
index_tuple<0, 1, 2, …, N-1>

                Tuple Indices


» Use variadic templates to unpack a tuple
get<0>(t), get<1>(t), get<2>(t), …, get<N-1>(t)

                      At Compile Time!
template <typename Tuple1,
          typename Tuple2,
          unsigned... Indices1,
          unsigned... Indices2>      Any number of indices

 Blob(Tuple1 tuple1,
      Tuple2 tuple2,
      index_tuple<Indices1...>,
      index_tuple<Indices2...>)

   : _v(get<Indices1>(tuple1)...),       Size of tuple must
     _l(get<Indices2>(tuple2)...)        match # of indices
 { }


» We need some way to get index_tuple from a tuple
» E.g., index_tuple<0, 1, 2> from tuple(v1, v2, v3)
» Use the one provided by your compiler!
   ˃ Not standard!

                         gcc                   Clang

Index Builder   _Build_index_tuple   __make_tuple_indices

Index Tuple     _Index_tuple         __tuple_indices


» Or write one yourself
   ˃ ~20 lines of template meta-programming
» Just Google!
class Blob {
  template <typename Tuple1,
            typename Tuple2,
            unsigned... Indices1,
            unsigned... Indices2>
  Blob(Tuple1 tuple1,
       Tuple2 tuple2,
       index_tuple<Indices1...>,        We don’t want users to
       index_tuple<Indices2...>)         pass the index_tuple
  : _v(get<Indices1>(tuple1)...),
    _l(get<Indices2>(tuple2)...) { }

  template <typename Tuple1,
            typename Tuple2>
  Blob(Tuple1 tuple1,                           Using
       Tuple2 tuple2)                         Delegated
  : Blob(tuple1,                             Constructor
         tuple2,
         typename make_indices<Tuple1>::type(),
         typename make_indices<Tuple2>::type())
  {}
};
int main(void)
{
  Blob b1(std::make_tuple(5, "C++ Truths"),
          std::make_tuple(3, 99.99));
}
» Avoid Copies
   ˃ std::make_tuple makes copies of parameters


» How do we perfect forward parameters through a
  tuple?
   ˃ Use std::forward_as_tuple
 int main(void)
 {
   Blob b1(std::forward_as_tuple(5, "C++ Truths"),
           std::forward_as_tuple(3, 99.99));
 }



                                  std::tuple<int &&, double &&>
class Blob {
public:
  template <typename... Args1,
             typename... Args2>
  Blob(std::tuple<Args1...> tuple1,
       std::tuple<Args2...> tuple2)
  : Blob(std::move(tuple1),
         std::move(tuple2),
         typename make_indices<Args1...>::type(),
         typename make_indices<Args2...>::type())
  {}
private:
  template <typename... Args1,
             typename... Args2,
             unsigned... Indices1,
             unsigned... Indices2>
  Blob(std::tuple<Args1...> tuple1,
       std::tuple<Args2...> tuple2,
       index_tuple<Indices1...>,
       index_tuple<Indices2...>)
    : _v(std::forward<Args1>(std::get<Indices1>(tuple1))...),
      _l(std::forward<Args2>(std::get<Indices2>(tuple2))...)
  { }
};
» It does!
» “Piecewise Construct”; “Emplace Construct”
» In fact, standard library has a type called
  std::piecewise_construct_t
» The standard library uses this idiom in
   ˃ std::map
   ˃ std::unordered_map
   ˃ std::pair has a piecewise constructor!
» Does it break encapsulation?
   ˃ Arguably, yes!
» The idiom should be used selectively
   ˃ Suitable for value-types implemented as structs (e.g., std::pair)
   ˃ When you know the implementation detail precisely
      + E.g., Auto-generated C++ lasses from DTD, XSD, IDL
» C++ Truths
  ˃ Rvalue References In Constructor: When Less Is More
     http://cpptruths.blogspot.com/2012/03/rvalue-references-in-constructor-when.html
  ˃ Perfect Forwarding of Parameter Groups in C++11
     http://cpptruths.blogspot.com/2012/06/perfect-forwarding-of-parameter-groups.html




» A Sense of Design
  ˃ Efficient Argument Passing in C++11 (Parts 1, 2, and 3)
     http://codesynthesis.com/~boris/blog/2012/06/26/efficient-argument-passing-cxx11-part2

C++11 Idioms @ Silicon Valley Code Camp 2012

  • 1.
    Sumant Tambe, Ph.D. SeniorSoftware Research Engineer Real-Time Innovations, Inc. http://cpptruths.blogspot.com
  • 2.
    Blogger Library Developer Author
  • 3.
    This talk isabout programming language idioms Definitions taken from Answers.com and Google.com
  • 4.
    » Idioms ˃ Specific grammatical, syntactic, structural expressions in a given programming language to solve an implementation problem ˃ Often have names—create vocabulary like patterns ˃ Repeat themselves ˃ Often cannot be modularized as libraries ˃ May get promoted as first-class features as a language evolves » Idioms vs. Patterns ˃ Idioms are patterns specific to a language ˃ Patterns are language independent
  • 5.
    » Idioms ofUsing C++11 Rvalue References » C++ Truths ˃ Rvalue References In Constructor: When Less Is More ˃ Perfect Forwarding of Parameter Groups in C++11 » A Sense of Design – Boris Kolpackov ˃ Efficient Argument Passing in C++11 (Parts 1, 2, and 3) (with permission)
  • 7.
    class Book { //.... private: std::string _title; std::vector<std::string> _authors; std::string _publisher; size_t _pub_year; };
  • 8.
    class Book { public: Book(const std::string & title, const std::vector<std::string> & authors, Good old C++03 const std::string & pub, constructor const size_t & pub_year); // .... private: std::string _title; std::vector<std::string> _authors; std::string _publisher; size_t _pub_year; }; » Other alternatives ˃ An iterator-pair instead of const std::vector<std::string>& ˃ but lets keep it simple
  • 9.
    class Book { public: Book(const std::string & title, const std::vector<std::string> & authors, Constructor const std::string & pub, parameters const size_t & pub_year) : _title(title), _authors(authors), _publisher(pub), make copies in *this _pub_year(pub_year) {} private: std::string _title; std::vector<std::string> _authors; std::string _publisher; size_t _pub_year; }; Is this class move enabled?
  • 10.
    class Book { public: Book(const std::string & title, const std::vector<std::string> & authors, No need to const std::string & pub, write these const size_t & pub_year); declarations Book (const Book &) = default; // Copy Constructor Book & operator = (const Book &) = default; // Copy Assign Book (Book &&) = default; // Move Constructor Book & operator = (Book &&) = default; // Move Assign ~Book() = default; // Destructor private: std::string _title; Compiler will std::vector<std::string> _authors; provide them std::string _publisher; where size_t _pub_year; appropriate };
  • 11.
    But, I JustREAD About Rvalue References!
  • 12.
    class Book { public: Book(const std::string & title, const std::vector<std::string> & authors, const std::string & pub, const size_t & pub_year); // Old c-tor Book(std::string && title, std::vector<std::string> && authors, std::string && pub, size_t && pub_year) Constructor with C++11 : _title(std::move(title)), rvalue references _authors(std::move(authors)), _publisher(std::move(publisher), _pub_year(std::move(pub_year)) {} // ... Members not shown }; Is it now optimally move enabled?
  • 13.
    class Book { public: Book(const std::string & title, const std::vector<std::string> & authors, const std::string & pub, const size_t & pub_year); // Old constructor Book(std::string && title, std::vector<std::string> && authors, std::string && pub, size_t && pub_year) // New constructor : _title(std::move(title)), _authors(std::move(authors)), _publisher(std::move(publisher), _pub_year(std::move(pub_year)) {} }; int main(void) { std::vector<std::string> authors { "A", "B", "C" }; Calls old Book b1("Book1", authors, "O’Reilly", 2012); constructor! const size_t year = 2012; Calls old Book b2("Book1", { "Author" }, "O’Reilly", year); constructor! Book b3("Book", { "Author" }, "O’Reilly", 2012); // Calls New Ctor }
  • 14.
    Book::Book(std::string && title, std::vector<std::string> && authors, std::string && pub, size_t && pub_year); // New constructor lvalue int main(void) { std::vector<std::string> authors { "A", "B", "C" }; Book b1("Book1", authors, "O’Reilly", 2012); lvalue const size_t year = 2012; Book b2("Book1", { "Author" }, "O’Reilly", year); No lvalue Book b3("Book", { "Author" }, "O’Reilly", 2012); } » Even one incompatible parameter (lvalue) will cause the compiler to reject the new rvalue-only constructor
  • 15.
    » Fortunately anoptimal solution exists! » But has a small problem  8 constructors! ˃ Implementations of all the constructors are different! ˃ Each Rvalue reference object must be std::moved ˃ In general exponential number of constructors title authors pub year string && vector<string> && string && size_t string && vector<string> && const string & size_t string && const vector<string> & string && size_t string && const vector<string> & const string & size_t const string & vector<string> && string && size_t const string & vector<string> && const string & size_t const string & const vector<string> & string && size_t const string & const vector<string> & const string & size_t
  • 16.
    » Help thecompiler take the best decision ˃ Pass-by-value! ˃ std::move each parameter ˃ Compiler makes no more copies than absolutely necessary ˃ Copy-elision may avoid moves too! class Book { public: Book(std::string title, std::vector<std::string> authors, std::string pub, size_t pub_year) Only one constructor : _title (std::move(title)), _authors (std::move(authors)), _publisher(std::move(pub)), _pub_year (std::move(pub_year)) {} };
  • 17.
    class Book { public: Book(std::string title, std::vector<std::string> authors, std::string publisher, size_t pub_year) : _title (std::move(title)), _authors (std::move(authors)), _publisher(std::move(publisher)), _pub_year (pub_year) {} }; std::string publisher() { // ... } lvalue int main(void) { std::vector<std::string> authors { "A", "B", "C" }; Book b1("Book1", authors, publisher(), 2012); } ctor + Copy-ctor + 2 moves 2 copies 1 move 1 move
  • 18.
    » Move isnot free! ˃ Potentially 5 to 15 % performance degradation ˃ Benchmark! » Many types don’t have efficient move operations ˃ std::array, std::complex, etc. ˃ Many C++03 libraries have not caught up with C++11 + Compiler will not provide implicit move operations (in most cases) » Move may be same as copy! ˃ Move construction  Use move-ctor if available, otherwise use copy- ctor!! ˃ Move==Copy when strings are small (small string optimization) » Works best only when you know you are going to make a copy
  • 19.
    A sense ofdesign
  • 20.
    matrix operator+ (constmatrix& x, const matrix& y) { matrix r (x); r += y; return r; } » matrix is movable (efficiently) » matrix is copyable too » operator + makes a copy ˃ Lets try to save it if we can!
  • 21.
    matrix operator+ (matrix&&x, const matrix& y) { matrix r (std::move(x)); r += y; Look ma! return r; No Copy! } matrix a, b; matrix c = a * 2 + b; Works great! matrix d = a + b * 2; Oops!
  • 22.
    matrix operator+ (constmatrix& x, const matrix& y) { matrix r (x); r += y; ARE YOU return r; KIDDING } ME? matrix operator+ (const matrix& x, matrix&& y) { matrix r (std::move(y)); r += x; return r; } matrix operator+ (matrix&& x, const matrix& y) { matrix r (std::move(x)); r += y; return r; } matrix operator+ (matrix&& x, matrix&& y) { matrix r (std::move(x)); r += y; return r; } In general, exponential number of implementations
  • 23.
    » Pass-by-value! matrix operator+(matrix x, matrix y) { matrix r (std::move(x)); r += y; return r; } matrix a, b; matrix c = a*2 + b*4; Works great! matrix d = a + b*4; Two Unnecessary Moves matrix e = a*2 + b; matrix f = a + b; One Unnecessary Copy and Move We need a way to detect lvalue/rvalue at runtime
  • 25.
    struct X {... }; » Pass original type to a void g(X&& t); // A forwarded function void g(X& t); // B ˃ Forward lvalues as lvalues template<typename T> ˃ Forward const lvalues as const lvalues void f(T&& t) ˃ Forward rvalues as rvalues { g(std::forward<T>(t)); » Works very well with factory } functions int main() template <class Arg> { Blob * createBlob(Arg&& arg) X x; { f(x); // Calls B return new Blob(std::forward<Arg>(arg)); f(X()); // Calls A } }
  • 26.
    » Perfect forwarding can be made to work … But » First, you must use a template » Second, you need two template parameters (T and U) » Then you must restrict them to the specific type you are interested in (i.e., matrix) ˃ Most likely using enable_if » Finally, you need to ask the right question ˃ IS_RVALUE(T, x)? #define IS_RVALUE(TYPE, VAR) (std::is_rvalue_reference<decltype(std::forward<TYPE>((VAR)))>::value) (!std::is_reference<TYPE>::value) In short, It is too much noise!
  • 27.
    Parameter Example Best Case Sub-optimal Case Const f(const matix &); Function makes Function makes a copy and Reference no copy and pass pass rvalue lvalue (missed optimization) Pass-by-value f(matrix); Function makes a Function makes no copy copy and pass and pass lvalue rvalue (unnecessary copy) Const f(const matrix &); All cases For N parameters, 2N Reference AND implementations AND f(matrix &&); Rvalue Reference Perfect template <class T> When you need a When you don’t want a Forwarding f(T&&); template template (complex use of (T &&) enable_if) Can we do better?
  • 28.
    » A typethat 1. Binds to lvalues as const reference 2. Binds to rvalues as rvalue reference 3. Determines lvalue/rvalue at run-time 4. Does not force the use of templates 5. Causes no code bloat 6. Is built-in » Const reference does not satisfy #3 » Perfect forwarding does not satisfy #4 » std::reference_wrapper does not satisfy #2 and #3
  • 29.
    template <typename T>struct in { in (const T& l): v_ (l), rv_ (false) {} in (T&& r) : v_ (r), rv_ (true) {} bool lvalue () const { return !rv_; } bool rvalue () const { return rv_; } operator const T& () const { return v_; } const T& get () const { return v_; } T&& rget () const { return std::move (const_cast<T&> (v_)); } T move () const { if (rv_) return rget (); else return v_; } private: const T& v_; bool rv_; };
  • 30.
    matrix operator +(in<matrix> m1, in<matrix> m2) { if(m1.rvalue()) { matrix r(m1.rget()); r += m2.get(); return r; } » No template! else if(m2.rvalue()) { matrix r(m2.rget()); » Simple, self-explanatory r += m1.get(); return r; » Can be even shorter! } else { matrix r(m1.get()); r += m2.get(); return r; } }
  • 31.
    » Idiom usefulonly when copies of parameters are made conditionally ˃ See authors blog for more details » Not clean when ambiguous implicit conversions are involved ˃ See authors blog for more details » Not well-known yet ˃ May be surprising to some » More critical eyes needed ˃ Propose in Boost? » Should be built-in, ideally ˃ Propose a language extension for C++1y?
  • 33.
    class Blob { private: std::vector<std::string> _v; }; » Initialize Blob  Initializing Blob::_v » C++11 std::vector has 9 constructors! » Question: How to write Blob’s constructor(s) so that all the vector’s constructors could be used? int main(void) { const char * shapes[3] = { "Circle", "Triangle", "Square" }; Blob b1(5, "C++ Truths"); // Initialize Blob::_v with 5 strings Blob b2(shapes, shapes+3); // Initialize Blob::_v with 3 shapes }
  • 34.
    class Blob { std::vector<std::string> _v; public: template<class Arg1, class Arg2> Blob(Arg1&& arg1, Arg2&& arg2) Perfect forward : _v(std::forward<Arg1>(arg1), two parameters std::forward<Arg2>(arg2)) { } }; int main(void) { const char * shapes[3] = { "Circle", "Triangle", "Square" }; Blob b1(5, "C++ Truths"); // OK Blob b2(shapes, shapes+3); // OK }
  • 35.
    class Blob { std::vector<std::string> _v; public: template<class Arg1, class Arg2> Blob(Arg1&& arg1, Arg2&& arg2) : _v(std::forward<Arg1>(arg1), Perfect forward std::forward<Arg2>(arg2)) two parameters { } }; int main(void) { Blob b3; // Default constructor. Does not compile! Blob b4(256); // 256 empty strings. Does not compile! }
  • 36.
    class Blob { std::vector<std::string> _v; public: template<class... Args> Blob(Args&&... args) : _v(std::forward<Args>(args)...) { } }; int main(void) { const char * shapes[3] = { "Circle", "Triangle", "Square" }; Blob b1(5, "C++ Truths"); // OK Blob b2(shapes, shapes+3); // OK Blob b3; // OK Blob b4(256); // OK }
  • 37.
    class Blob { std::vector<std::string> _v; std::list<double> _l public: template<class... Args> Blob(Args&&... args) : _v(???), What parameters _l(???) are for _v and { } what are for _l? }; int main(void) { // Initialize Blob::_v with 5 strings and // Blob::_l with { 99.99, 99.99, 99.99 } Blob b1(5, "C++ Truths", 3, 99.99); // Does not compile! }
  • 38.
    int main(void) { Blob b1(5, "C++ Truths", 3, 99.99); } int main(void) { Blob b1(std::make_tuple(5, "C++ Truths"), std::make_tuple(3, 99.99)); } » But std::vector and std::list don’t accept std::tuple as a parameter to constructor
  • 39.
    » Packing parametersinto a tuple is easy ˃ Just call std::make_tuple » Unpacking is easy too ˃ Use std::get<N> int main(void) { std::tuple<int, double> t = std::make_tuple(3, 99.99); std::list<int> list (std::get<0>(t), std::get<1>(t)); } » But the number and types of parameters may vary » We need a generic way to unpack tuples ˃ This is extraordinarily complicated
  • 40.
    » If youhave a tuple t = (v1, v2, v3, …, vN) » You need get<0>(t), get<1>(t), get<2>(t), …, get<N-1>(t) At Compile Time!
  • 41.
    » If youhave a tuple t = (v1, v2, v3, …, vN) » And if you have another type index_tuple<0, 1, 2, …, N-1> Tuple Indices » Use variadic templates to unpack a tuple get<0>(t), get<1>(t), get<2>(t), …, get<N-1>(t) At Compile Time!
  • 42.
    template <typename Tuple1, typename Tuple2, unsigned... Indices1, unsigned... Indices2> Any number of indices Blob(Tuple1 tuple1, Tuple2 tuple2, index_tuple<Indices1...>, index_tuple<Indices2...>) : _v(get<Indices1>(tuple1)...), Size of tuple must _l(get<Indices2>(tuple2)...) match # of indices { } » We need some way to get index_tuple from a tuple » E.g., index_tuple<0, 1, 2> from tuple(v1, v2, v3)
  • 43.
    » Use theone provided by your compiler! ˃ Not standard! gcc Clang Index Builder _Build_index_tuple __make_tuple_indices Index Tuple _Index_tuple __tuple_indices » Or write one yourself ˃ ~20 lines of template meta-programming » Just Google!
  • 44.
    class Blob { template <typename Tuple1, typename Tuple2, unsigned... Indices1, unsigned... Indices2> Blob(Tuple1 tuple1, Tuple2 tuple2, index_tuple<Indices1...>, We don’t want users to index_tuple<Indices2...>) pass the index_tuple : _v(get<Indices1>(tuple1)...), _l(get<Indices2>(tuple2)...) { } template <typename Tuple1, typename Tuple2> Blob(Tuple1 tuple1, Using Tuple2 tuple2) Delegated : Blob(tuple1, Constructor tuple2, typename make_indices<Tuple1>::type(), typename make_indices<Tuple2>::type()) {} }; int main(void) { Blob b1(std::make_tuple(5, "C++ Truths"), std::make_tuple(3, 99.99)); }
  • 45.
    » Avoid Copies ˃ std::make_tuple makes copies of parameters » How do we perfect forward parameters through a tuple? ˃ Use std::forward_as_tuple int main(void) { Blob b1(std::forward_as_tuple(5, "C++ Truths"), std::forward_as_tuple(3, 99.99)); } std::tuple<int &&, double &&>
  • 46.
    class Blob { public: template <typename... Args1, typename... Args2> Blob(std::tuple<Args1...> tuple1, std::tuple<Args2...> tuple2) : Blob(std::move(tuple1), std::move(tuple2), typename make_indices<Args1...>::type(), typename make_indices<Args2...>::type()) {} private: template <typename... Args1, typename... Args2, unsigned... Indices1, unsigned... Indices2> Blob(std::tuple<Args1...> tuple1, std::tuple<Args2...> tuple2, index_tuple<Indices1...>, index_tuple<Indices2...>) : _v(std::forward<Args1>(std::get<Indices1>(tuple1))...), _l(std::forward<Args2>(std::get<Indices2>(tuple2))...) { } };
  • 47.
    » It does! »“Piecewise Construct”; “Emplace Construct” » In fact, standard library has a type called std::piecewise_construct_t » The standard library uses this idiom in ˃ std::map ˃ std::unordered_map ˃ std::pair has a piecewise constructor!
  • 48.
    » Does itbreak encapsulation? ˃ Arguably, yes! » The idiom should be used selectively ˃ Suitable for value-types implemented as structs (e.g., std::pair) ˃ When you know the implementation detail precisely + E.g., Auto-generated C++ lasses from DTD, XSD, IDL
  • 49.
    » C++ Truths ˃ Rvalue References In Constructor: When Less Is More http://cpptruths.blogspot.com/2012/03/rvalue-references-in-constructor-when.html ˃ Perfect Forwarding of Parameter Groups in C++11 http://cpptruths.blogspot.com/2012/06/perfect-forwarding-of-parameter-groups.html » A Sense of Design ˃ Efficient Argument Passing in C++11 (Parts 1, 2, and 3) http://codesynthesis.com/~boris/blog/2012/06/26/efficient-argument-passing-cxx11-part2