Moving to Modern C++

Distinguish between () and {} when creating objects

class Widget {
public:
 Widget(int i, bool b); // ctors not declaring
 Widget(int i, double d); // std::initializer_list params
 …
};
Widget w1(10, true); // calls first ctor
Widget w2{10, true}; // also calls first ctor
Widget w3(10, 5.0); // calls second ctor
Widget w4{10, 5.0}; // also calls second ctor
class Widget {
public:
 Widget(int i, bool b); // as before
 Widget(int i, double d); // as before
Widget(std::initializer_list<long double> il); // added
 …
};
Widget w1(10, true); // uses parens and, as before,
 // calls first ctor
Widget w2{10, true}; // uses braces, but now calls
 // std::initializer_list ctor
// (10 and true convert to long double)
Widget w3(10, 5.0); // uses parens and, as before,
 // calls second ctor
Widget w4{10, 5.0}; // uses braces, but now calls
 // std::initializer_list ctor
// (10 and 5.0 convert to long double)
class Widget {
public:
 Widget(int i, bool b); // as before
 Widget(int i, double d); // as before
 Widget(std::initializer_list<long double> il); // as before
 operator float() const; // convert
 … // to float
};
Widget w5(w4); // uses parens, calls copy ctor
Widget w6{w4}; // uses braces, calls std::initializer_list ctor (w4 converts to float, and float converts to long double)
Widget w7(std::move(w4)); // uses parens, calls move ctor
Widget w8{std::move(w4)}; // uses braces, calls std::initializer_list ctor (for same reason as w6)
class Widget {
public:
 Widget(int i, bool b); // as before
 Widget(int i, double d); // as before
 // std::initializer_list element type is now std::string
 Widget(std::initializer_list<std::string> il);
 … // no implicit
}; // conversion funcs
Widget w1(10, true); // uses parens, still calls first ctor
Widget w2{10, true}; // uses braces, now calls first ctor
Widget w3(10, 5.0); // uses parens, still calls second ctor
Widget w4{10, 5.0}; // uses braces, now calls second ctor
class Widget {
public:
 Widget(); // default ctor
 Widget(std::initializer_list<int> il); // std::initializer
 // _list ctor
 … // no implicit
}; // conversion funcs
Widget w1; // calls default ctor
Widget w2{}; // also calls default ctor
Widget w3(); // most vexing parse! declares a function!
Widget w4({}); // calls std::initializer_list ctor with empty list
Widget w5{{}}; // ditto
std::vector<int> v1(10, 20); // use non-std::initializer_list
 // ctor: create 10-element
// std::vector, all elements have
// value of 20
std::vector<int> v2{10, 20}; // use std::initializer_list ctor:
 // create 2-element std::vector,
// element values are 10 and 20
template<typename T, // type of object to create
 typename... Ts> // types of arguments to use
void doSomeWork(Ts&&... params)
{
 create local T object from params...
 …
}
T localObject(std::forward<Ts>(params)...); // using parens
T localObject{std::forward<Ts>(params)...}; // using braces

Prefer nullptr to 0 and NULL

void f(int); // three overloads of f
void f(bool);
void f(void*);
f(0); // calls f(int), not f(void*)
f(NULL); // might not compile, but typically calls
 // f(int). Never calls f(void*)
f(nullptr); // calls f(void*) overload

Prefer alias declarations to typedefs

Avoiding such medical tragedies is easy. Introduce a typedef:

typedef std::unique_ptr> UPtrMapSS;

But typedefs are soooo C++98. They work in C++11, sure, but C++11 also offers alias declarations:

using UPtrMapSS = std::unique_ptr>;
// FP is a synonym for a pointer to a function taking an int and
// a const std::string& and returning nothing
typedef void (*FP)(int, const std::string&); // typedef
// same meaning as above
using FP = void (*)(int, const std::string&); // alias
 // declaration
template<typename T> // MyAllocList<T>
using MyAllocList = std::list<T, MyAlloc<T>>; // is synonym for
 // std::list<T,
// MyAlloc<T>>
MyAllocList<Widget> lw; // client code

template<typename T> // MyAllocList<T>::type
struct MyAllocList { // is synonym for
 typedef std::list<T, MyAlloc<T>> type; // std::list<T,
}; // MyAlloc<T>>
MyAllocList<Widget>::type lw; // client code

• typedefs don’t support templatization, but alias declarations do.
• Alias templates avoid the “::type” suffix and, in templates, the “typename”
prefix often required to refer to typedefs

template <class T>
using remove_const_t = typename remove_const<T>::type;
template <class T>
using remove_reference_t = typename remove_reference<T>::type;
template <class T>
using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;

Prefer scoped enums to unscoped enums

enum Color { black, white, red }; // black, white, red are in same scope as Color
auto white = false; // error! white already declared in this scope

enum class Color { black, white, red }; // black, white, red are scoped to Color
auto white = false; // fine, no other
Color c = white; // error! no enumerator named "white" is in this scope
Color c = Color::white; // fine
auto c = Color::white; // also fine (and in accord with Item 5's advice)
enum Color { black, white, red }; // unscoped enum
std::vector<std::size_t> // func. returning
 primeFactors(std::size_t x); // prime factors of x
Color c = red;
…
if (c < 14.5) { // compare Color to double (!)
 auto factors = // compute prime factors
 primeFactors(c); // of a Color (!)
 …
}
enum class Color { black, white, red }; // enum is now scoped
Color c = Color::red; // as before, but
… // with scope qualifier
if (c < 14.5) { // error! can't compare Color and double
auto factors = // error! can't pass Color to
 primeFactors(c); // function expecting std::size_t
 …
}
if (static_cast<double>(c) < 14.5) { // odd code, but
 // it's valid
 auto factors = // suspect, but
 primeFactors(static_cast<std::size_t>(c)); // it compiles
 …
}

Prefer deleted functions to private undefined ones.

template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
 …
private:
 basic_ios(const basic_ios& ); // not defined
 basic_ios& operator=(const basic_ios&); // not defined
};
//instead prefer to use delete
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
 …
 basic_ios(const basic_ios& ) = delete;
 basic_ios& operator=(const basic_ios&) = delete;
 …
};
void fun(int k)
void fun(char)=delete;
void fun(double)=delete;
fun(12);//ok
fun('a');//error
fun(4.5);//error
class Widget {
public:
 …
 template<typename T>
 void processPointer(T* ptr)
 { … }
 …
};
template<> // still
void Widget::processPointer<void>(void*) = delete; // public, but deleted