Skip to content

Commit

Permalink
Add Item-2 part fix readme errors
Browse files Browse the repository at this point in the history
  • Loading branch information
XimingCheng committed Dec 9, 2014
1 parent 51943f9 commit 9b7f35d
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 3 deletions.
120 changes: 119 additions & 1 deletion DeducingTypes/2-Understand-auto-type-deduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,122 @@ void func_for_rx(const T& param); // 推导rx概念上的模板
func_for_rx(x); // 概念调用:param的推导类型就是rx的类型
```
正如我所说,对`auto`的类型推导只存在一种情况的例外(这个后面就会讨论),其他的就和模板类型推导完全一样了。
正如我所说,对`auto`的类型推导只存在一种情况的例外(这个后面就会讨论),其他的就和模板类型推导完全一样了。
条款1把模板类型推导划分成三部分,基于在通用的函数模板的`ParamType`的特性和`param`的类型声明。在一个用`auto`声明的变量上,类型声明代替了`ParamType`的作用,所以也有三种情况:
* 情况1:类型声明是一个指针或者是一个引用,但不是一个通用的引用
* 情况2:类型声明是一个通用引用
* 情况3:类型声明既不是一个指针也不是一个引用
我们已经看了情况1和情况3的例子:
```cpp
auto x = 27; // 情况3(x既不是指针也不是引用)
const auto cx = x; // 情况3(cx二者都不是)
const auto& rx = x; // 情况1(rx是一个非通用的引用)
```

情况2正如你期待的那样:

```cpp
auto&& uref1 = x; // x是int并且是左值
// 所以uref1的类型是int&

auto&& uref2 = cx; // cx是int并且是左值
// 所以uref2的类型是const int&

auto&& uref3 = 27; // 27是int并且是右值
// 所以uref3的类型是int&&
```

条款1讲解了在非引用类型声明里,数组和函数名称如何退化成指针。这在`auto`类型推导上面也是一样:

```cpp
const char name[] = // name的类型是const char[13]
"R. N. Briggs";

auto arr1 = name; // arr1的类型是const char*

auto& arr2 = name; // arr2的类型是const char (&)[13]

void someFunc(int, double); // someFunc是一个函数,类型是
// void (*)(int, double)

auto& func2 = someFunc; // func1的类型是
// void (&)(int, double)
```
正如你所见,`auto`类型推导和模板类型推导工作很类似。它们就像一枚硬币的两面。
除了有一种情况是不一样的。我们从如果你想声明一个用27初始化的`int`, C++98你有两种语法选择:
```cpp
int x1 = 27;
int x2(27);
```

C++11,通过标准支持的统一初始化(使用花括号初始化——译者注),可以添加下面的代码:

```cpp
int x3 = { 27 };
int x4{ 27 };
```
综上四种语法,都会生成一种结果:一个拥有27数值的`int`。
但是正如条款5所解释的,使用`auto`来声明变量比使用固定的类型更好,所以在上述的声明中把`int`换成`auto`更好。最直白的写法就如下面的代码:
```cpp
auto x1 = 27;
auto x2(27);
auto x3 = {27};
auto x4{ 27 };
```

上面的所有声明都可以编译,但是他们和被替换的相对应的语句的意义并不一样。头两个的确是一样的,声明一个初始化值为27的`int`。然而后面两个,声明了一个类型为`std::intializer_list<int>`的变量,这个变量包含了一个单一的元素27!

```cpp
auto x1 = 27; // 类型时int,值是27

auto x2(27); // 同上

auto x3 = { 27 }; // 类型是std::intializer_list<int>
// 值是{ 27 }

auto x4{ 27 }; // 同上
```
这和`auto`的一种特殊类型推导有关系。当使用一对花括号来初始化一个`auto`类型的变量的时候,推导的类型是`std::intializer_list`。如果这种类型无法被推导(比如在花括号中的变量拥有不同的类型),代码会编译错误。
```cpp
auto x5 = { 1, 2, 3.0 }; // 错误! 不能讲T推导成
// std::intializer_list<T>
```

正如注释中所说的,在这种情况,类型推导会失败,但是认识到这里实际上是有两种类型推导是非常重要的。一种是`auto: x5`的类型被推导。因为`x5`的初始化是在花括号里面,`x5`必须被推导成`std::intializer_list`。但是`std::intializer_list`是一个模板。实例是对一些`T`实例化成`std::intializer_list<T>`,这就意味着`T`的类型必须被推导出来。类型推导就在第二种的推导的范围上失败了。在这个例子中,类型推导失败是因为在花括号里面的数值并不是单一类型的。

对待花括号初始化的行为是`auto`唯一和模板类型推导不一样的地方。当`auto`声明变量被使用一对花括号初始化,推导的类型是`std::intializer_list`的一个实例。但是如果相同的初始化递给相同的模板,类型推导会失败,代码不能编译。

```cpp
auto x = { 11, 23, 9 }; // x的类型是
// std::initializer_list<int>

template<typename T> // 和x的声明等价的
void f(T param); // 模板

f({ 11, 23, 9 }); // 错误的!没办法推导T的类型
```
但是,如果你明确模板的`param`的类型是一个不知道`T`类型的`std::initializer_list<T>`:
```cpp
template<typename T>
void f(std::initializer_list<T> initList);
f({ 11, 23, 9 }); // T被推导成int,initList的
// 类型是std::initializer_list<int>
```
所以`auto`和模板类型推导的本质区别就是`auto`假设花括号初始化代表的是std::initializer_list,但是模板类型推导却不是。
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Effective Modern C++ 中文翻译,欢迎大家提出翻译中的错误和用
使用gitbook作为静态编译输出,需要安装`Node.js`,然后从`npm`安装gitbook

```sh
npm install gitbook
npm install gitbook -g
```

然后git clone下来本书,然后输出静态网页,在浏览器上查看:
Expand All @@ -23,4 +23,4 @@ cd Effective-Modern-Cpp-Zh
gitbook serve .
```

gitbook会默认在端口`4000`开启服务器,使用浏览器访问![http://localhost:4000/](http://localhost:4000/)就可以访问然后阅读本书的中文翻译。随后我会将本书编译生成的静态网页上传至github pages。
gitbook会默认在端口`4000`开启服务器,使用浏览器访问[http://localhost:4000/](http://localhost:4000/)就可以访问然后阅读本书的中文翻译。随后我会将本书编译生成的静态网页上传至github pages。

0 comments on commit 9b7f35d

Please sign in to comment.