1 / 93

Modern C++ A (Hopefully) Practical Introduction

Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st , 2013. Modern C++ A (Hopefully) Practical Introduction. Agenda. Today: auto , decltype , range-based for , etc. Lambdas Rvalue references and moving Variadic templates Tomorrow Threads, atomics and the memory model

selah
Download Presentation

Modern C++ A (Hopefully) Practical Introduction

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. YaserZhian Dead Mage IGDI, Workshop 10, May 30th-31st, 2013 Modern C++A (Hopefully) Practical Introduction

  2. Agenda • Today: • auto, decltype, range-based for, etc. • Lambdas • Rvalue references and moving • Variadic templates • Tomorrow • Threads, atomics and the memory model • Other features: initializer lists, constexpr, etc. • Library updates: new containers, smart pointers, etc. • General Q&A http://yaserzt.com/

  3. Themes and Takeaways • Ways to write code that is: • Cleaner and less error-prone • Faster • Richer and can do more (occasionally) • Know thy language • You can never have too many tools • Elegance in interface; complexity (if any) in implementation • Take everything here with a grain of salt! http://yaserzt.com/

  4. Examples and Exercises (1/2) • We will use Visual Studio 2012 (with the Nov 2012 CTP compiler update.) • Go ahead. Open it up, make a project, add a file, set the “toolset” in project options. • Write a simple “hello, world” and run it. • Please do try and write code; the sound of keyboard does not disrupt the workshop. http://yaserzt.com/

  5. Examples and Exercises (2/2) • We will also use “IDE One” online compiler at http://ideone.com/ • You might want to register an account there. • Do so while I talk about unimportant stuff and answer any questions… • Remember to select the C++11 compiler. • Write and run a simple program here as well. http://yaserzt.com/

  6. auto, decltypeand range-based for http://yaserzt.com/

  7. decltype(1/3) • What is the type of a + b? • a? int? double ? • Dependent on operator +, and on a and b. • And a whole lot of name lookup, type deduction and overload resolution rules. • Even if you don’t know, the compiler always does. • decltype(a + b) c;c = a + b; • (Instead of e.g. double c;) http://yaserzt.com/

  8. decltype (2/3) • What’s the return type of this function?template <typenameT, typenameU>??? Add (T const & a, U const & b) {returna + b;} • One answer is decltype(T() + U()) • Not entirely correct. (Why?) • The correct answer is decltype(a + b) • But that won’t compile. http://yaserzt.com/

  9. decltype (3/3) • What is wrong with this?template <typenameT, typenameU>decltype(a + b)Add(T const & a, U const & b) {returna + b;} • This is basically the motivation behind the new function declaration syntax in C++11. http://yaserzt.com/

  10. New Function Syntax • autoFun (type1p1) -> returntype; • The previous function template then becomes: • template <typenameT, typenameU>autoAdd (T const & a, U const & b)-> decltype(a + b){returna + b;} • This works for ordinary functions too: • autoSqr (float x)->float{return x*x;} http://yaserzt.com/

  11. auto (1/2) • Putting auto where a type name is expected, instructs the compiler to infer type from initializing expression, e.g. • auto foo = a * b + c * d; • auto bar =newstd::map<std::string, bool>; • autobaz = newstd::map<std::pair<std::string, int>,std::vector<bool, my_bool_alloc>>::const_iterator; http://yaserzt.com/

  12. auto (2/2) • Some more examples: • autox = 0; • auto y = do_stuff (x); • auto const & y = do_stuff (x); • auto f = std::bind (foo, _1, 42); • for (autoi = c.begin(), e = c.end();i != e; ++i) {…} http://yaserzt.com/

  13. decltype and auto – a caveat • Sometimes, you have to be very careful with auto and decltype: std::vector<int> const & v (1); autoa = v[0];// int decltype(v[1]) b = 1;// intconst & auto c = 0; // int autod = c;// int decltype(c) e = 1;// int decltype((c)) f = c;// int & decltype(0) g;// int http://yaserzt.com/

  14. Range-based for Loop (1/2) • How common is this code snippet? vector<string> v;for(vector<string>::iteratori = v.begin();i!= u.end(); i++) cout<< *i << endl; • How many problems can you see? • Here’s a better version: for (auto i = v.cbegin(), e = v.cend(); i != e; ++i) cout<< *i << endl; • This is the best version: for (autoconst & s : v) cout<< s << endl; http://yaserzt.com/

  15. Range-based for Loop (2/2) • This loop: for(for-range-declaration: expression)statement • will get expanded to something like this: {auto&& __range = range-init;for(auto __begin= begin-expr, __end= end-expr; __begin != __end; ++__begin) {for-range-declaration= *__begin;statement }} http://yaserzt.com/

  16. Lambdas Introducing more “functionality” into C++ http://yaserzt.com/

  17. Lambdas (1/5) • Lambdas are unnamed functions that you can write almost anywhere in your code (that you can write an expression.) • For example: • [] (intx) -> int {returnx * x;} • [] (intx,inty){returnx<y? y : x;} • What does this do? • [] (doublev) {cout << v;} (4.2); http://yaserzt.com/

  18. Lambdas (2/5) • Storing lambdas: • autosqr = [] (intx) -> int {returnx * x;};autoa = sqr(42); • std::function<int(int, int)> g =[] (inta, intb) {returna + b;};intd = g(43, -1); • auto h = std::bind ( [](intx,inty){returnx<y ? y: x;} , _1, 0);auton = h (-7); http://yaserzt.com/

  19. Lambdas (3/5) • Consider these functions: • template <typenameC, typenameF>voidApply (C & c, Fconst & f) {for(auto & v : c) f(v);} • template<typenameC, typenameT>void Apply2 (C & c, function<void(T&)> const & f) {for(auto & v : c) f(v);} • Used like this: • int a [] = {10, 3, 17, -1};Apply (a, [] (int & x) {x += 2;}); http://yaserzt.com/

  20. Lambdas (4/5) • Apply (a, [](intx) {cout << x << endl;}); • int y = 2;Apply (a, [y](int & x) {x += y;}); • int s = 0;Apply (a, [&s](intx) {s += x;}); • Apply (a, [y, &s](intx) {s += x + y;}); http://yaserzt.com/

  21. Lambdas (5/5) • inty = 2;autof = [y](int & x) {x += y;};y = 10;Apply (a, f); • inty = 2;auto f = [&y](int & x) {x += y;};y = 10;Apply (a, f); • By the way, you can capture everything by value ([=]) or by reference ([&]). http://yaserzt.com/

  22. Rvalue References http://yaserzt.com/

  23. Rvalue References and Moving (1/B) • C++ used to have a tendency to copy stuff around if you weren’t paying attention! • What happens when we call this function? • vector<string> GenerateNames(){returnvector<string>(50, string(100, '*'));} • A whole lot of useless stuff are created and copied around. • All sorts of techniques and tricks to avoid those copies. http://yaserzt.com/

  24. Rvalue References and Moving (2/B) • string s = string("Hello") + " "+ "world."; • string(charconst*) • stringoperator + (stringconst&, charconst*) • stringoperator + (stringconst &, charconst *) • this ultimately called the copy c’torstring (stringconst &). • In total, there can be as many as 5 (or even 7) temporary strings here. • (Unrelated note) Some allocations can be avoided with “Expression Templates”. http://yaserzt.com/

  25. (Copy Elision and RVO) • When dealing with anonymous temporary objects, the compiler can “elide” their (copy-) construction, which is called “copy elision”. • This is a unique kind of optimization, as the compiler is allowed to remove code that has side effects! • Return Value Optimization is one kind of copy elision. http://yaserzt.com/

  26. Rvalue References and Moving (3/B) • C++11 introduces “rvalue references” to let you work with (kinda) temporary objects. • Rvalue references are denoted with &&. • e.g. int && p = 3; • or void foo (std::string && s); • or Matrix::Matrix (Matrix && that){…} http://yaserzt.com/

  27. Rvalue References and Moving (4/B) • In situations where you used to copy the data from an object into another object, if your first object is an rvalue (i.e. temporary) now you can “move” the data from that to this. • Two important usages of rvalue references are “move construction” and “move assignment”. • e.g. string(string && that);// move c'tor • and string& operator = (string && that); // move assignment http://yaserzt.com/

  28. Rvalue References and Moving (5/B) template <typename T> class Matrix { private: T * m_data; unsignedm_rows, m_columns; public: Matrix (unsigned rows, unsigned columns); ~Matrix (); Matrix (Matrix<T> const & that); template <typenameU> Matrix (Matrix<U> const & that); Matrix<T> & operator = (Matrix<T> const & that); Matrix (Matrix<T> && that); Matrix<T> & operator = (Matrix<T> && that); ... }; http://yaserzt.com/

  29. Rvalue References and Moving (6/B) template <typename T> class Matrix { ... unsigned rows () const; unsignedcolumns () const; unsignedsize () const; T & operator () (unsigned row, unsigned col); // m(5, 7) = 0; T const & operator () (unsigned row, unsigned col) const; template<typenameU> autooperator + (Matrix<U> const & rhs) const-> Matrix<decltype(T() + U())>; template<typenameU> autooperator * (Matrix<U> const & rhs) const-> Matrix<decltype(T() * U() + T() * U())>; }; http://yaserzt.com/

  30. Rvalue References and Moving (7/B) Matrix (unsigned rows, unsigned columns) : m_rows (rows), m_columns (columns) , m_data (new T [rows * columns]) { } ~Matrix () { delete[] m_data; } Matrix (Matrix<T> const & that) : m_rows (that.m_rows), m_columns (that.m_columns) , m_data (new T [that.m_rows * that.m_columns]) { std::copy (that.m_data, that.m_data + (m_rows * m_columns), m_data ); } http://yaserzt.com/

  31. Rvalue References and Moving (8/B) Matrix<T>& operator = (Matrix<T>const & that) { if(this != &that) { T * new_data = new T [that.m_rows * that.m_columns]; std::copy (that.m_data, that.m_data + (m_rows * m_columns), new_data ); delete[] m_data; m_data = new_data; m_rows= that.m_rows; m_columns = that.m_columns; } return*this; } http://yaserzt.com/

  32. Rvalue References and Moving (9/B) Matrix (Matrix<T> && that) : m_rows (that.m_rows), m_columns (that.m_columns) , m_data (that.m_data) { that.m_rows= that.m_columns = 0; that.m_data= nullptr; } http://yaserzt.com/

  33. Rvalue References and Moving (A/B) Matrix<T> & operator = (Matrix<T> && that) { if(this != &that) { delete[] m_data; m_rows= that.m_rows; m_columns= that.m_columns; m_data= that.data; that.m_rows= rhs.m_columns = 0; that.m_data= nullptr; } return*this; } http://yaserzt.com/

  34. Rvalue References and Moving (B/B) structSomeClass { strings; vector<int> v; public: // WRONG! WRONG! WRONG! // Doesn’t move, just copies. SomeClass (SomeClass && that) : s (that.s), v (that.v) {} SomeClass(SomeClass && that) : s (std::move(that.s)), v (std::move(that.v)) {} }; http://yaserzt.com/

  35. std::move (1/8) • In principle, std::move should look like this:template <typenameT>??? move (??? something){returnsomething;} • What should the argument type be? • T&& ? • T& ? • Both? Neither? • We need to be able to pass in both lvalues and rvalues. http://yaserzt.com/

  36. std::move (2/8) • We can overload move() like this: • move (T && something) • move (T & something) • But that will lead to exponential explosion of overloads if the function has more arguments. • “Reference collapse” rule in C++98: • int& & is collapsed to int&. • In C++11, the rules are: (in addition to the above) • int&& & is collapsed to int&. • int& && is collapsed to int&. • int&& && is collapsed to int&&. http://yaserzt.com/

  37. std::move (3/8) • Therefore, only the T&& version should be enough. • If you pass in an lvalue to our move, the actual argument type will collapse into T&, which is what we want (probably.) • So, move looks like this thus far:template <typename T>??? move (T && something){return something;} http://yaserzt.com/

  38. std::move (4/8) • Now, what is the return type? • T&& ? • It should be T&& in the end. • But if we declare it so, and move() is called on an lvalue, • then T will be SomeType& • then T&& will be SomeType& && • then it will collapse into SomeType& • then we will be returning an lvalue reference from move(), which will prevent any moving at all. • We need a way to remove the & if T already has one. http://yaserzt.com/

  39. std::move (5/8) • We need a mechanism to map one type to another • In this case, to map T& and T&& to T, and T to T. • There is no simple way to describe the process, but this is how it’s done: • template<typename T> structRemoveReference{typedefT type;}; • With that, RemoveReference<int>::type will be equivalent to int. • But we are not done. http://yaserzt.com/

  40. std::move (6/8) • Now we specialize:template<typenameT>structRemoveReference<T&>{typedefTtype;}; • template<typenameT>structRemoveReference<T &&>{typedefTtype;}; • Now, RemoveReference<int&>::type will be int too. http://yaserzt.com/

  41. std::move (7/8) • Our move now has the correct signature:template<typenameT>typenameRemoveReference<T>::type &&move (T && something){returnsomething;} • But it’s not correct. That “something” in there is an lvalue, remember? http://yaserzt.com/

  42. std::move (8/8) • …so we cast it to an rvalue reference:template <typenameT>typenameRemoveReference<T>::type && move (T && something){return static_cast<typenameRemoveReference<T>::type && > (something);} • Hopefully, this is correct now! http://yaserzt.com/

  43. Universal References • There is no such thing as universal references! • But, due to the C++11 reference collapsing, sometimes when you write T && v, you can get anything; both lvalues and rvalues. • These can be thought of as “universal references”. • Two preconditions: • There must be T&&, • And there must be type deduction. http://yaserzt.com/

  44. End of Day 1 Any questions? http://yaserzt.com/

  45. ScopeGuard A Simple Method to Do RAII and Transactions http://yaserzt.com/

  46. Motivation (in C) • This is an extremely common pattern in programming: • if(<action>) {if (!<next>)<rollback><cleanup>} • For example: • if (OpenDatabase()) {if(!WriteNameAndAge())UnwriteNameAndAge();CloseDatabase ();} http://yaserzt.com/

  47. Motivation (in C+) • The “object-oriented” way might be: • classRAII{ RAII () {<action>} ~RAII () {<cleanup>}}; • … • RAIIraii;try {<next>} catch (...) {<rollback>throw;} http://yaserzt.com/

  48. Even More Motivation • What happens if you need to compose actions? • if (<action1>) {if(<action2>) {if(!<next2>) {<rollback2><rollback1>}<cleanup2>} else<rollback1><cleanup1>} http://yaserzt.com/

  49. The C++ Way (1/2) • What if we could write this: • <action>SCOPE_EXIT {<cleanup>};SCOPE_FAIL {<rollback>};<next> http://yaserzt.com/

  50. The C++ Way (2/2) • Extremely easy to compose: • <action1>SCOPE_EXIT {<cleanup1>};SCOPE_FAIL {<rollback1>};<action2>SCOPE_EXIT {<cleanup2>};SCOPE_FAIL {<rollback2>};<next2> http://yaserzt.com/

More Related