Your SlideShare is downloading. ×
0
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
C++11 Idioms @ Silicon Valley Code Camp 2012
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

C++11 Idioms @ Silicon Valley Code Camp 2012

37,032

Published on

C++11 feels like a new language. Compared to its previous standards, C++11 packs more language features and libraries designed to make C++ programs easier to understand and faster. As the community is …

C++11 feels like a new language. Compared to its previous standards, C++11 packs more language features and libraries designed to make C++ programs easier to understand and faster. As the community is building up experience with the new features, new stylistic ways of using them are emerging. These styles (a.k.a. idioms) give the new language its unique flavor. This talk will present emerging idioms of using rvalue references -- a marquee feature of C++11 as many renowned experts call it. You will see how C++11 opens new possibilities to design class interfaces. Finally, you will learn some advanced use-cases of rvalue references which will likely make you feel something amiss in this flagship feature of C++11.

Published in: Technology
7 Comments
22 Likes
Statistics
Notes
  • Really a good one..
    Programming Pad
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • The solution to matrix problem is painfully simple, there is no need for run-time tricks, when everything is decided at compile time:
    matrix operator+ (matrix m1, const matrix& m2) { return std::move(m1 += m2); }
    matrix operator+ (const matrix& m1, matrix&& m2) { return std::move(m2 += m1); }

    See here: http://ideone.com/sPLEYm
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • @Haohao if you're just going to keep your opinion, no reason to restate it. Note that there will be a big difference between library code (especially generic libraries, which will be a growing portion) and 'user code'. The latter will certainly be a lot easier.

    Point in case: it is trivial to write your 'correct constructor' by replacing C++03 rule of 3 (with pitfalls and all) by C++11's [Rule Of Zero](http://rmartinho.github.com/2012/08/15/rule-of-zero.html), which will lead to correctness, efficiency, exception neutrality at zero cost. Read the article!

    The slides show a large portion of /library style/ code. It is showing the mechanics _behind_ things. Use the proper abstractions. These slides share ideas for new/popular abstractions and how they could be implemented. That does not give you a picture of what the usage would look like.

    Also, as mentioned on slide 30 (Caveats) the use of some of the ideas exposed is, in fact, rather disputable. Don't take the idea of `in` parameter passing style as a 'symptom of C++'. You can (and IMO should) live without them.

    Still kudos for @Sumant for putting the ideas out there in the wild. This will spark the inspiration for others, and people will come up with alternatives/improvements/critique. So: kudos. Keep up the nice work, Sumant
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Why do you need an axillary class to do perfect forwarding? If you leave the constructor to just take template parameters you get perfect forwarding for free, plus you can bind to any type that can implicitly convert itself into your required types. If you're really concerned about error messages (if you're developing a library for example) then enable_if has your back. I'm not convinced that we need more machinery for a trivial operation.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • @SumantTambe I admit C++11 is way better than 03, but this slide made me feel that even writing a perfect constructor, I mean right and efficient, is harder than before. Pass-by-value is right and cool, but still not perfect according to your slides. I just felt a little sad since the zero overhead principle is known to all, nothing is perfect though...
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
37,032
On Slideshare
0
From Embeds
0
Number of Embeds
87
Actions
Shares
0
Downloads
203
Comments
7
Likes
22
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Sumant Tambe, Ph.D.Senior Software Research EngineerReal-Time Innovations, Inc.http://cpptruths.blogspot.com
  • 2. Blogger Library DeveloperAuthor
  • 3. This talk is about 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 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)
  • 6. class Book {// ....private: std::string _title; std::vector<std::string> _authors; std::string _publisher; size_t _pub_year;};
  • 7. 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
  • 8. 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?
  • 9. 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; // Destructorprivate: std::string _title; Compiler will std::vector<std::string> _authors; provide them std::string _publisher; where size_t _pub_year; appropriate};
  • 10. But, I Just READ About Rvalue References!
  • 11. 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?
  • 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 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}
  • 13. Book::Book(std::string && title, std::vector<std::string> && authors, std::string && pub, size_t && pub_year); // New constructor lvalueint 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
  • 14. » 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 yearstring && vector<string> && string && size_tstring && vector<string> && const string & size_tstring && const vector<string> & string && size_tstring && const vector<string> & const string & size_tconst string & vector<string> && string && size_tconst string & vector<string> && const string & size_tconst string & const vector<string> & string && size_tconst string & const vector<string> & const string & size_t
  • 15. » 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)) {}};
  • 16. 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() { // ... } lvalueint 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
  • 17. » 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
  • 18. A sense of design
  • 19. 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!
  • 20. 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!
  • 21. 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
  • 22. » 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 Movesmatrix e = a*2 + b;matrix f = a + b; One Unnecessary Copy and Move We need a way to detect lvalue/rvalue at runtime
  • 23. struct X { ... }; » Pass original type to avoid g(X&& t); // A forwarded functionvoid g(X& t); // B ˃ Forward lvalues as lvaluestemplate<typename T> ˃ Forward const lvalues as const lvaluesvoid f(T&& t) ˃ Forward rvalues as rvalues{ g(std::forward<T>(t)); » Works very well with factory} functionsint 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 }}
  • 24. » 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!
  • 25. Parameter Example Best Case Sub-optimal CaseConst f(const matix &); Function makes Function makes a copy andReference 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, 2NReference AND implementationsAND f(matrix &&);RvalueReferencePerfect template <class T> When you need a When you don’t want aForwarding f(T&&); template template (complex use of(T &&) enable_if) Can we do better?
  • 26. » 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
  • 27. 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_;};
  • 28. 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; }}
  • 29. » 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?
  • 30. 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}
  • 31. 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}
  • 32. 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!}
  • 33. 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}
  • 34. class Blob { std::vector<std::string> _v; std::list<double> _lpublic: 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!}
  • 35. 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
  • 36. » 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
  • 37. » If you have a tuplet = (v1, v2, v3, …, vN)» You needget<0>(t), get<1>(t), get<2>(t), …, get<N-1>(t) At Compile Time!
  • 38. » If you have a tuplet = (v1, v2, v3, …, vN)» And if you have another typeindex_tuple<0, 1, 2, …, N-1> Tuple Indices» Use variadic templates to unpack a tupleget<0>(t), get<1>(t), get<2>(t), …, get<N-1>(t) At Compile Time!
  • 39. 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)
  • 40. » Use the one provided by your compiler! ˃ Not standard! gcc ClangIndex Builder _Build_index_tuple __make_tuple_indicesIndex Tuple _Index_tuple __tuple_indices» Or write one yourself ˃ ~20 lines of template meta-programming» Just Google!
  • 41. 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));}
  • 42. » 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 &&>
  • 43. 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))...) { }};
  • 44. » 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!
  • 45. » 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
  • 46. » 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

×