mirror of
https://github.com/CnTransGroup/EffectiveModernCppChinese.git
synced 2024-12-28 05:40:43 +08:00
format
This commit is contained in:
parent
678ed45e01
commit
b716af6b67
@ -19,7 +19,7 @@ std::array<int, sz> data1; // 错误!一样的问题
|
||||
constexpr auto arraySize2 = 10; // 没问题,10是编译
|
||||
// 期可知常量
|
||||
std::array<int, arraySize2> data2; // 没问题, arraySize2是constexpr
|
||||
```
|
||||
```
|
||||
注意**const**不提供**constexpr**所能保证之事,因为**const**对象不需要在编译期初始化它的值。
|
||||
```cpp
|
||||
int sz; // 和之前一样
|
||||
@ -33,7 +33,7 @@ std::array<int, arraySize2> data2; // 没问题, arraySize2是constexpr
|
||||
+ **constexpr**函数可以用于需求编译期常量的上下文。如果你传给**constexpr**函数的实参在编译期可知,那么结果将在编译期计算。如果实参的值在编译期不知道,你的代码就会被拒绝。
|
||||
+ 当一个**constexpr**函数被一个或者多个编译期不可知值调用时,它就像普通函数一样,运行时计算它的结果。这意味着你不需要两个函数,一个用于编译期计算,一个用于运行时计算。**constexpr**全做了。
|
||||
|
||||
假设我们需要一个数据结构来存储一个实验的结果,而这个实验可能以各种方式进行。实验期间风扇转速,温度等等都可能导致亮度值改变,亮度值可以是高,低,或者无。如果有n个实验相关的环境条件。它们每一个都有三个状态,最终可以得到的组合有`3^n`个。储存所有实验结果的所有组合需要这个数据结构足够大。假设每个结果都是**int**并且**n**是编译期已知的(或者可以被计算出的),一个`std::array`是一个合理的选择。我们需要一个方法在编译期计算`3^n`。C++标准库提供了`std::pow`,它的数学意义正是我们所需要的,但是,对我们来说,这里还有两个问题。第一,`std::pow`是为浮点类型设计的 我们需要整型结果。第二,`std::pow`不是**constexpr**(即,使用编译期可知值调用得到的可能不是编译期可知的结果),所以我们不能用它作为`std::array`的大小。
|
||||
假设我们需要一个数据结构来存储一个实验的结果,而这个实验可能以各种方式进行。实验期间风扇转速,温度等等都可能导致亮度值改变,亮度值可以是高,低,或者无。如果有n个实验相关的环境条件。它们每一个都有三个状态,最终可以得到的组合$$3^n$$个。储存所有实验结果的所有组合需要这个数据结构足够大。假设每个结果都是**int**并且**n**是编译期已知的(或者可以被计算出的),一个`std::array`是一个合理的选择。我们需要一个方法在编译期计算`3^n`。C++标准库提供了`std::pow`,它的数学意义正是我们所需要的,但是,对我们来说,这里还有两个问题。第一,`std::pow`是为浮点类型设计的 我们需要整型结果。第二,`std::pow`不是**constexpr**(即,使用编译期可知值调用得到的可能不是编译期可知的结果),所以我们不能用它作为`std::array`的大小。
|
||||
|
||||
幸运的是,我们可以应需写个`pow`。我将展示怎么快速完成它,不过现在让我们先看看它应该怎么被声明和使用:
|
||||
```cpp
|
||||
|
@ -36,6 +36,7 @@ w1 = w2; //是一个赋值运算符,调用operator=函数
|
||||
C++11使用统一初始化(uniform initialization)来整合这些混乱且繁多的初始化语法,所谓统一初始化是指使用单一初始化语法在任何地方_[0]_表达任何东西。
|
||||
它基于花括号,出于这个原因我更喜欢称之为括号初始化_[1]_。统一初始化是一个概念上的东西,而括号初始化是一个具体语法构型。
|
||||
括号初始化让你可以表达以前表达不出的东西。使用花括号,指定一个容器的元素变得很容易:
|
||||
|
||||
````cpp
|
||||
std::vector<int> v{1,3,5}; //v包含1,3,5
|
||||
````
|
||||
@ -72,6 +73,7 @@ int sum3 = x + y + z; //同上
|
||||
另一个值得注意的特性是括号表达式对于C++最令人头疼的解析问题_[2]_有天生的免疫性。
|
||||
C++规定任何能被决议为一个声明的东西必须被决议为声明。这个规则的副作用是让很多程序员备受折磨:当他们想创建一个使用默认构造函数构造的对象,却不小心变成了函数声明。
|
||||
问题的根源是如果你想使用一个实参调用一个构造函数,你可以这样做:
|
||||
|
||||
````cpp
|
||||
Widget w1(10); //使用实参10调用Widget的一个构造函数
|
||||
````
|
||||
@ -112,7 +114,8 @@ Widget w4{10, 5.0}; // 同上
|
||||
````cpp
|
||||
class Widget {
|
||||
public:
|
||||
Widget(int i, bool b);
|
||||
Widget(int i, bool b); // 同上
|
||||
Widget(int i, double d); // 同上
|
||||
Widget(std::initializer_list<long double> il); //新添加的
|
||||
…
|
||||
};
|
||||
@ -131,15 +134,16 @@ Widget w3(10, 5.0); // 使用小括号初始化
|
||||
|
||||
Widget w4{10, 5.0}; // 使用花括号初始化
|
||||
// 调用第二个构造函数
|
||||
// (10 和 true 转化为long double)
|
||||
// (10 和 5.0 转化为long double)
|
||||
````
|
||||
甚至普通的构造函数和移动构造函数都会被std::initializer_list构造函数劫持:
|
||||
````cpp
|
||||
class Widget {
|
||||
public:
|
||||
Widget(int i, bool b);
|
||||
Widget(int i, bool b);
|
||||
Widget(int i, double d);
|
||||
Widget(std::initializer_list<long double> il);
|
||||
operator float() const;
|
||||
operator float() const; // convert to float (译者注:高亮)
|
||||
};
|
||||
Widget w5(w4); // 使用小括号,调用拷贝构造函数
|
||||
|
||||
@ -155,8 +159,8 @@ class Widget {
|
||||
public:
|
||||
Widget(int i, bool b);
|
||||
Widget(int i, double d);
|
||||
Widget(std::initializer_list<bool> il);
|
||||
…
|
||||
Widget(std::initializer_list<bool> il); // element type is now bool
|
||||
… // no implicit conversion funcs
|
||||
};
|
||||
Widget w{10, 5.0}; //错误!要求变窄转换
|
||||
````
|
||||
@ -164,8 +168,8 @@ Widget w{10, 5.0}; //错误!要求变窄转换
|
||||
调用这个函数将会把`int(10)`和double(5.0)`转换为bool,由于括号初始化拒绝变窄转换,所以这个调用无效,代码无法通过编译。
|
||||
|
||||
只有当没办法把括号初始化中实参的类型转化为std::initializer_list时,编译器才会回到正常的函数决议流程中。
|
||||
比如我们在构造函数中用`std::initializer_list<std::string`代替`std::initializer_list<bool>`,这时非std::initializer_list构造函数将再次成为函数决议的候选者,
|
||||
因为没有办法把int和bool转换为std::string:
|
||||
比如我们在构造函数中用`std::initializer_list<std::string`代替`std::initializer_list<bool>`,这时非std::initializer_list构造函数将再次成为函数决议的候选者,因为没有办法把int和bool转换为std::string:
|
||||
|
||||
````cpp
|
||||
class Widget {
|
||||
public:
|
||||
@ -205,6 +209,7 @@ Widget w5{{}}; // 同上
|
||||
可能比你想象的要多。因为std::vector也会受到影响。
|
||||
std::vector有一个非std::initializer_list构造函数允许你去指定容器的初始大小,以及使用一个值填满你的容器。
|
||||
但它也有一个std::initializer_list构造函数允许你使用花括号里面的值初始化容器。如果你创建一个数值类型的vector,然后你传递两个实参。把这两个实参放到小括号和放到花括号中是不同:
|
||||
|
||||
````cpp
|
||||
std::vector<int> v1(10, 20); //使用非std::initializer_list
|
||||
//构造函数创建一个包含10个元素的std::vector
|
||||
|
Loading…
Reference in New Issue
Block a user