The Style of C++ 11

  • 5,003 views
Uploaded on

SELA C++ Conference Keynote: The Style of C++ 11, by Sasha Goldshtein.

SELA C++ Conference Keynote: The Style of C++ 11, by Sasha Goldshtein.

More in: Technology , Education
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
5,003
On Slideshare
0
From Embeds
0
Number of Embeds
1

Actions

Shares
Downloads
91
Comments
0
Likes
6

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
  • The standardsyntax also allows for (auto& n : numbers) { ++n; }, i.e. going over the elements with a mutable iterator.The Microsoft syntax allows this as well, with for each (auto& n in numbers) { ++n; }.
  • VC11 does not support initializer lists yet.More examples for uniform initialization:struct X { X(inti, int j); };void f(X);f({1,2});std::complex<double> c {1.0,1.0};
  • std::bind and std::mem_fn are really state-of-the-art (from TR1) considering the state of affairs using vanilla C++98.Namely, we had pointer_to_unary_function, bind1st, bind2nd, and similar stuff.
  • Mention that stateless lambdas (beginning with [], such that they don’t capture any local variables) are implicitly convertible to function pointers. (But only in VS11 :-))
  • There is a slightly complicated point in the move constructor here. If the move constructor were to use the assignment directly, e.g. *this = other, then the standard operator= would be called. The reason is that rvalue references are treated as lvalues as long as they have a name. What std::move does is pass its parameter through, and by doing that it removes its name so the caller treats it as an unnamed object, and hence an rvalue. (The reason for this quirk, in turn, is to prevent accidental moves that will destroy the object. E.g. you could move from other, and then attempt to use other later in your method.)After we have a container that works like this, we can also have an std::swap that works appropriately—it will simply use std::move instead of just the assignment operator to move the swapped data around (through a temporary variable), i.e.: temp = move(a); a = move(b); b = move(temp); — and no copies of the contents are required.
  • Note that Microsoft had stdext::hash_map previously, and nearly everyone else did, too. Unclear how this will merge in.There is another new kind of collection—std::array<T,N>—which is a compile-time sized array with begin() and end() member functions for use with STL collections requiring an iterator in a natural fashion.

Transcript

  • 1. © Copyright SELA software & Education Labs Ltd. 14-18 Baruch Hirsch St.Bnei Brak 51202 Israel www.sela.co.il
  • 2. C++11 status(Some) New language features(Some) New library featuresModern C++ style
  • 3. • After more than a decade of contemplation . . .• ISO C++11 Standard was published in September 2011• Feature list: http://en.wikipedia.org/wiki/C%2B%2B11• The Standard Library is part of the C++11 standard• Some new features are upstream merges from TR1
  • 4. • Visual Studio 2010: Some features are supported• Visual Studio 2012: Some more features are supported Visual Studio 2012 Not supported yet Visual Studio 2010 Automatic Concurrency Variadic variables, library templates decltype Memory Custom Rvalue model literals references Delegating Lambda constructors functions• Comparison chart between many other compilers: http://s.sashag.net/rpST0u
  • 5. Implicit variable declaration: The compiler knows what you mean (Almost) necessary for anonymous types Very convenient for complex templates Easily abused by lazy programmers!std::map<...> M;auto iter = M.begin(); //what’s the type of iter?auto pair = std::make_pair(iter, M.key_range(...));auto lambda = []() { ... }; //lambdas have an anonymous typeauto ptr = condition ? new class1 : new class2; //ERRORauto x = 15; auto s = (string)"Hello"; //try to avoid...
  • 6. Automatic iterator over arrays and STL collections Your collection will work – provide begin(), end(), and an input iterator over the elements Up to VC11 Beta: for each … in, a non-standard Microsoft extensionint numbers[] = ...;for (int n : numbers) std::cout << n;std::map<std::string,std::list<int>> M;for (const auto& pair : M) for (auto n : pair.second) std::cout << pair.first << << pair.second;
  • 7. Use a compile-time expression instead of a type name Can take the type of any expression Very useful for templates, forwarding etc.float arr[15];decltype(arr[0]) flt_ref = arr[0];decltype(arr[1]+7) flt;decltype(rand()) n = rand();decltype(i+j) sum = i+j;
  • 8. Your function can return a decltype Requires special syntax in these examples because the return type depends on parameter typestemplate <typename T1, typename T2>auto multiply(const T1& t1, const T2& t2) -> decltype(t1+t2) { return t1 * t2;}template <typename InputIterator>auto nth_element(InputIterator iter, int n) -> decltype(*iter) { while (--n > 0) ++iter; return *iter;}
  • 9. Initialize arrays, lists, vectors, other containers—and your own containers—with a natural syntax Not yet supported by Visual Studio 2012vector<int> v { 1, 2, 3, 4 };list<string> l = { “Tel-Aviv”, “Jerusalem” };my_cont c { 42, 43, 44 };class my_cont { public: my_cont(std::initializer_list<int> list) { for (auto it = list.begin(); it != list.end(); ++it) . . . }};
  • 10. int main() { [](){}(); []{}();} //this is legal C++, //although not useful
  • 11. The current state of function objects and operations on them leaves much to be desired Must use arcane binding functions, placeholders, and rules to construct composite functorsclass employee { public: void bonus(float commission, int vacation);};vector<int> employees;std::for_each(employees.begin(), employees.end(), std::bind(std::mem_fn(&employee::bonus), _1, 0.25f, 3));
  • 12. TR1 makes it somewhat easier to manipulate functors (functions and classes with operator()) Doesn’t make it easier to create functorsstd::function<bool(int,int)> g = greater<int>();std::function<int(int,char**)> m = main;std::function<bool(int)> greater_than17 = std::bind(g, _1, 17);std::function<void(X*)> f = &X::foo; //foo is a member function
  • 13. Inline methods in other methods (closures) Compile to an anonymous class that serves as a function object Rich capture semantics by value and by referenceauto print_num = [](int n) { std::cout << n; };std::list<int> ns = ...;std::for_each(ns.begin(), ns.end(), print_num);int even = std::count_if(ns.begin(), ns.end(), [](int n) { return n&1==0; });int x = 5;[&x]() { ++x; }(); //capture by reference[ x]() { ++x; }(); //capture by value. doesn’t compile!!
  • 14. Default capture (use at your own risk) Mutable lambdas Explicit return valueint fib1 = 1, fib2 = 1;auto next_step = [&]() { //default capture by reference int temp = fib2; fib2 = fib2 + fib1; fib1 = temp;};for (int i = 0; i < 20; ++i) next_step();int n = 10;auto say_yes_n_times = [=]() mutable ->bool { //default capture by value, return (--n > 0); //mutable and returns bool};
  • 15. std::function<...> to the rescue Freely manipulate lambdas as objectsauto identity = [](int x) { return [x]() { return x; };};auto next = [](const std::function<int(void)>& lambda) { return [&lambda]() { return lambda() + 1; };};auto _1 = identity(1);auto _2 = next(_1);auto _3 = next(_2);std::cout << _1() << _2() << _3();
  • 16. Recall our contrived bind(mem_fn(…)) example Use a lambda instead of composite functors Design your APIs with lambdas in mindstd::for_each(employees.begin(), employees.end(), std::bind(memfn(&employee::bonus), _1, 0.25f, 3));std::for_each(employees.begin(), employees.end(), [](const employee& e) { e.bonus(0.25f, 3); });template <typename Callback>void enum_windows(const string& title, Callback callback) { . . . callback(current_window);}//or, use const std::function<void(const window&)>& as parameter
  • 17. Lvalues are values that have a name Can appear on the left-hand-side of an assignment Rvalues are the restint x;x = 42; //OK, x has a name, it’s an lvalue42 = x; //Obviously wrong, 42 does not have a name, it’s an rvaluex + 2 = 42; //Also wrong, x + 2 returns a temporary, it’s an rvaluex++ = 42; //Also wrong, x++ returns a temporary, it’s an rvalueint& foo();int* goo();--foo(); //OK, foo() returns an lvalue++(*goo()); //OK, a dereferenced pointer is an lvalue
  • 18. Turns out, this “standard” approach to references limits the performance of the language In this example, the contents of the vectors are COPIEDvoid init_vector(vector<int>& v);vector<int> v, w;init_vector(v); //no copy, we were careful to pass a referenceinit_vector(w); //no copy, we were careful to pass a referenceswap(v, w);//internally, swap will copy v to temp, w to v, temp to w, for a total//of THREE MEMORY ALLOCATIONS AND DEALLOCATIONS!//but how can we tell swap (and vector) to MOVE the contents around?
  • 19. Rvalue references are references to rvalues! Standard references are to lvalues, const references may refer to temporary rvalues Enable move construction and assignmentmy_array(const my_array& other) { //copy ctor dataptr_ = new T[size_ = other.size_]; memcpy_s(dataptr_, size_*sizeof(T), other.dataptr_, size_*sizeof(T));}my_array& operator=(const my_array& other) { /*same deal*/ }my_array& operator=(my_array&& other) { //move assignment dataptr_ = other.dataptr_; size_ = other.size_; other.dataptr_ = nullptr; other.size_ = 0;}my_array(my_array&& other) { //move ctor *this = std::move(other); //NOTE: && is lvalue in the method body}
  • 20. • Much fewer copies of temporary objects float around – E.g. consider std::vector<T> with reallocation – Huge performance boost when your types are used in STL – Huge performance boost when using strings and other types with inner state that is expensive to copy
  • 21. • Use auto, for each, initializer lists ubiquitously• Don’t be afraid of returning objects by value – RVO, NRVO, and move constructors will minimize copies• OK to design algorithms that require predicates, projections, and other functors – They will be easy to use—with lambda functions• Use STL algorithms more widely with lambdas
  • 22. Four standard unordered containers which use hash tables as their implementation unordered_map, unordered_set, unordered_multimap, unordered_multisetset<string> names = { “Mike”, “Adam” };assert(*names.begin() == “Adam”);unordered_set<string> names = { “John”, “Abe” };for (auto name : names) cout << name; //alphabetic order is NOT guaranteed
  • 23. PERL-style regular expression facility offered by std::regex class and associated functionsregex version("(d+).(d+).(d+)");string text = "2.0.50727";cmatch captures;if (regex_search(text.c_str(), captures, version)) { cout << "Major: " << captures[0] << endl; cout << "Build: " << captures[2] << endl;}//there’s also regex_replace for obvious purposes
  • 24. • The standard library now has three types of smart pointers, eliminating the need to ever use delete• If you are the sole owner of the object, use unique_ptr to make sure it’s deleted when the pointer dies (RAII)• If you want to share the object with others, use shared_ptr—it will perform smart reference counting• If you got yourself a cycle, use weak_ptr to break it!
  • 25. Sole owner of an object Supports move semantics, but not copy semantics Replaces auto_ptr (which can’t move!)unique_ptr<expensive_thing> create() { unique_ptr<expensive_thing> p(new expensive_thing); //...do some initialization, exceptions are covered by RAII return p;}unique_ptr<expensive_thing> p = create(); //move constructor used!//another example is storing pointers in containers:vector<unique_ptr<string>> v = { new string(“A”), new string(“B”) };
  • 26. Thread-safe reference-counted pointer to an object with shared ownership When the last pointer dies, the object is deletedstruct file_handle { HANDLE handle; file_handle(const string& filename) ... ~file_handle() ... //closes the handle};class file { shared_ptr<file_handle> _handle;public: file(const string& filename) : _handle(new file_handle(filename)) {} file(shared_ptr<file_handle> fh) : _handle(fh) {}}; //can have multiple file objects over the same file_handle
  • 27. Points to a shared object but does not keep it alive (does not affect reference count) The object may be destroyed “under our nose” at any time Breaks cycles between shared_ptrsclass employee { weak_ptr<employee> _manager; vector<shared_ptr<employee>> _direct_reports;public: void beg_for_vacation(int days) { if (auto mgr = _manager.lock()) { mgr->beg(days); } //mgr is shared_ptr else { /* your manager has been eliminated :-) */ } }};
  • 28. • Use smart pointers—no reason to have a delete statement in your code – If you’re the only owner, use unique_ptr – If you’re sharing the object, use shared_ptr – Create shared_ptrs with make_shared() – To prevent cycles, use weak_ptr• Use the non-member begin() and end() functions – They work on arrays, and can be overloaded for types you don’t control
  • 29. C++11 status(Some) New language features(Some) New library featuresModern C++ style
  • 30. • Bjarne Stroustrup’s FAQ: http://s.sashag.net/vWT1eI• C++11 Wikipedia article: http://s.sashag.net/vdSCW3• What’s New in VC++ 10: http://s.sashag.net/tb1fnr• What’s New in VC++ 11: http://s.sashag.net/sXy26y• More on rvalue references: http://s.sashag.net/uVLJ23• STL11 preliminary docs: http://s.sashag.net/vWR7sW• C++ memory model: http://s.sashag.net/rqsoDW• Modern C++ style: http://s.sashag.net/rP5DFl