1.1 特性一:可以在任何地方初始化
非静态成员变量可以用花括号初始化
1 2 3 4 5 6 7 8
| class Widget() { private: int x{ 0 }; int y = 0; int z(0); }
|
对于不可拷贝的对象进行初始化,也可以用花括号初始化(原子对象不可拷贝)
1 2 3 4
| std::atomic<int> ai1{ 0 }; std::atomic<int> ai2(0);
std::atomic<int> ai3 = 0;
|
1.2 特性二:不允许内置类型间隐式的变窄转换
1 2 3 4
| double x, y, z; int sum1{ x + y + z }; int sum2(x + y + z); int sum3 = x + y + z;
|
1.3 特性三:防止默认构造函数被混淆为函数声明
1 2 3 4 5 6
|
Widget w1();
Widget w2{};
|
1.4 特性四:花括号返回类型为std::initializer_list导致构造函数被劫持
情况一:
1 2 3 4 5 6 7 8 9 10 11 12
| class Widget { public: Widget(int i, bool b); Widget(int i, double d); Widget(std::initializer_list<bool> il); }
Widget w{10, 5.0};
|
情况二:
1 2 3 4 5 6 7 8 9 10
| class Widget { public: Widget(int i, bool b); Widget(int i, double d); Widget(std::initializer_list<string> il); }
Widget w{10, 5.0};
|
情况三:
1 2 3 4 5 6 7 8 9
| class Widget { public: Widget(int i, bool b); Widget(int i, double d); Widget (std::initializer_list<long, double> il); } Widget w{10, true};
|
情况四:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Widget { public: Widget(); Widget(std::initialr_list<int> il); }
Widget w1{};
Widget w2{{}}; Widget w3({});
|
1.5 特性五:某些类库中小括号和花括号的初始化差异
1 2
| std::vector<int> v1(10, 20); std::vector<int> v2{10, 20};
|
2 nullptr
使用场景:
例1: nullptr在函数重载时的好处
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream>
using namespace std;
void f(int) { cout << "exec int" << endl; }
void f(bool) { cout << "exec bool" << endl; }
void f(void*) { cout << "exec void*" << endl; }
int main() { f(0); f(nullptr); return 0; }
|
例2: nullptr表意明确
1 2 3 4 5 6 7 8
| auto result = findRecord();
if (result == 0) {}
if (result == nullptr) {}
|
例3: 在模板推导时避免NULL和0被推导为整型的错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| int f1(std::shared_ptr<Widget> spw); int f2(std::unique_ptr<Widget> upw); bool f3(Widget* pw);
template<typename FuncType, typename PtrType> decltype(auto) callFun(FuncType func, PtrType ptr) { return func(ptr); }
auto result1 = f1(0); auto result2 = f2(NULL); auto result3 = f3(nullptr);
auto result11 = callFun(f1, 0);
auto result22 = callFun(f2, NULL); auto result33 = callFun(f3, nullptr);
|
3 别名声明alias declaration
3.1 基本用法:和typedef一样
假如想定义一个std::unique_ptr<std::unordered_map<std::string>, <std::string>>
类型的对象,每次都要写一遍太麻烦了。
解决办法如下:
1 2 3 4 5 6 7 8
| typedef std::unique_ptr<std::unordered_map<std::string>, <std::string>> UPtrMapSS; using UPtrMapSS = std::unique_ptr<std::unordered_map<std::string>, <std::string>>;
UPtrMapSS x;
std::unique_ptr<std::unordered_map<std::string>, <std::string>> x;
|
3.2 对比typedef的优势
- 在某些情况语义更加明确
1 2 3
| typedef void (*FP)(int, const std::string&); using FP = void (*)(int, const std::string&);
|
- 别名声明可以被模板化(别名模板alias templates),但typedef不能
要想使MyAllocList<T> lt
等价于std::list<T, MyAlloc<T>> lt
,别名声明可以直接这样使用:
1 2 3 4 5
| template <typename T> using MyAllocList = std::list<T, MyAlloc<T>>;
MyAllocList<int> li;
|
而typedef要想用MyAllocList<T>
表示需要模板的std::list<T, MyAlloc<T>>
,必须将其包装在一个结构体中,并且调用也更加麻烦
1 2 3 4 5 6 7 8
| template<typename T> struct MyAllocList { typedef std::list<T, MyAlloc<T>> type; };
MyAllocList<int>::type li;
|
在类模板等场景使用时,由于MyAllocList<T>::type
是一个数据类型,且依赖模板参数T,所以是一个依赖数据类型,还需要在前面加typename,更加麻烦了。
而使用using时,编译器知道MyAllocList是一个别名模板,所以知道它一定是一个类型,所以MyAllocList<T>
就是一个非依赖类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| template <typename T> using MyAllocList = std::list<T, MyAlloc<T>>;
template<typename T> struct MyAllocList { typedef std::list<T, MyAlloc<T>> type; };
template<typename T> class Widget { private: typename MyAllocList<T>::type list; };
template<typename T> class Widget { private: MyAllocList<T> list; };
|
3.3 在C++11中实现C++14中的模板元编程(TMP)
1 2 3 4 5 6
| std::remove_const<T>::type std::remove_const_t<T> std::remove_reference<T>::type std::remove_reference_t<T> std::add_lvalue_reference<T>::type std::add_lvalue_reference_t<T>
|
- 通过以下别名声明,在C++11中调用
std::remove_const_t<T>
就等价于std::remove_const<T>::type
1 2 3 4 5 6 7 8
| 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;
|