Moving to Modern C According to Scott Meyers









- Slides: 9
Moving to Modern C++ According to Scott Meyers
Distinguish between () and {} when creating objects – The good �Uniform initialization – Single initialization syntax that can be used anywhere and express everything. �Uses “braced initialization”: int x {0}; �Can now initialize a vector: std: : vector<int> a. Vec {1, 3, 5}; �Prohibits implicit narrowing conversions of built-in types: double x, y, z; int sum {x + y + z} //ERROR. Sum of doubles may not be expressible as an int �Initialization by ‘=‘ or parentheses doesn’t check for narrowing.
Distinguish between () and {} when creating objects – The bad �std: : initializer_list – Compilers’ preference to match braced initialization with constructors that take an initializer list. �Example: class Widget{ Widget(int, double); Widget(int, bool); }; Widget w 1{1, 5. 0}; //Calls first ctor, also if parentheses are used Widget w 2 (5, true); //Calls 2 nd ctor, also if braces are used
�Now a fairly innocuous change: class Widget { Widget(int, double); Widget(int, bool); Widget(std: : initializer_list<long double>); operator float(); }; Widget w 3{5, 10. 0}; //Calls ctor that takes initializer list //conversion to long double Widget w 4(5, 10. 0); //Calls 1 st ctor as before
class Widget { Widget(int, double); Widget(int, bool); Widget(std: : initializer_list<bool>); operator float(); }; Widget w 3{5, 10. 0}; //As before, ctor with initializer list is //used, however a compiler error because //of narrowing check. Widget w 4(std: : move(w 3)); //Calls move ctor Widget w 5{std: : move(w 3)}; //Calls initializer_list ctor – w 3 //converts to float, float converts //to bool, error due to narrowing.
�To explicitly call ctor with initializer list: Widget w({}) OR Widget w{ {} }; �When this matters: std: : vector<int> the. Vec(10, 20); //Creates a 10 element //vector with all elements //having 20 as the initial value std: : vector<int> the. Vec{10, 20}; //Creates 2 -element vector //with 10 in the 1 st element //and 20 initialized in the nd
Prefer nullptr to 0 and NULL � 0 and NULL are integral types, not pointer types. �Example: void f(int); void f(bool); void f(void*); f(0); //Calls f(int) function f(NULL); //May not compile if NULL is defined as a long. //Otherwise, it will call f(int), never f(void*);
� Resolves ambiguity �Example: auto result = find. Result(/*params*/); if( result == 0)… What does find. Result return? �Use of nullptr makes the above scenario obvious: if( result == nullptr )… //find. Result returns a pointer � Template type deduction deduced the “wrong” types for 0 and NULL (C++14): int f 1(std: : shared_ptr<Widget> spw); double f 2(std: : unique_ptr<Widget> upw); bool f 3(Widget* w); template<typename Func. Type, typename Ptr. Type> decltype(auto) lock. And. Call(Func. Type f, Ptr. Type p){ return f(p); } lock. And. Call(f 1, 0); //ERROR lock. And. Call(f 2, NULL) //ERROR lock. And. Call(f 3, nullptr) //OK
Prefer alias declarations to typedefs �alias declarations can be templatized, while typedefs cannot: template<typename T> using My. Alias = std: : list<T, My. Alloc<T>>; My. Alias<Widget> a. Alloc; VERSUS template<typename T> struct My. Alloc. List{ typedef std: : list<T, My. Alloc<T>> type; }; My. Alloc. List<Widget>: : type b. Alloc;