diff --git a/1.DeducingTypes/item3.md b/1.DeducingTypes/item3.md index 7e3ba95..212d59b 100644 --- a/1.DeducingTypes/item3.md +++ b/1.DeducingTypes/item3.md @@ -29,9 +29,9 @@ if(v[0]==0) //decltype(v[0])是int& 在C++11中,decltype最主要的用途就是用于函数模板返回类型,而这个返回类型依赖形参。举个例子,假定我们写一个函数,一个参数为容器,一个参数为索引值,这个函数支持使用方括号的方式访问容器中指定索引值的数据,然后在返回索引操作的结果前执行认证用户操作。函数的返回类型应该和索引操作返回的类型相同。 -对一个T类型的容器使用**operator[]**通常会返回一个T&对象,比如**std::deque**就是这样,但是**std::vector**有一个例外,对于**std::vector**,**operator[]**不会返回**bool&**,它会返回一个有名字的对象类型(译注:MSVC的STL实现中返回的是**std::_Vb_reference>>**)。关于这个问题的详细讨论请参见Item6,这里重要的是我们可以看到对一个容器进行**operator[]**操作返回的类型取决于容器本身。 +对一个T类型的容器使用**operator[]** 通常会返回一个T&对象,比如**std::deque**就是这样,但是**std::vector**有一个例外,对于**std::vector**,**operator[]**不会返回**bool&**,它会返回一个有名字的对象类型(译注:MSVC的STL实现中返回的是**std::_Vb_reference>>**)。关于这个问题的详细讨论请参见Item6,这里重要的是我们可以看到对一个容器进行**operator[]**操作返回的类型取决于容器本身。 -使用**decltype**使得我们很容易去实现它,这是我们写的第一个版本,使用*decltype**计算返回类型,这个模板需要改良,我们把这个推迟到后面: +使用**decltype**使得我们很容易去实现它,这是我们写的第一个版本,使用**decltype**计算返回类型,这个模板需要改良,我们把这个推迟到后面: ````cpp template auto authAndAccess(Container& c,Index i) @@ -41,9 +41,10 @@ auto authAndAccess(Container& c,Index i) return c[i]; } ```` -函数名称前面的**auto**不会做任何的类型推导工作。相反的,他只是暗示使用了C++11的尾置返回类型语法,即在函数形参列表后面使用一个**->**符号指出函数的返回类型,尾置返回类型的好处是我们可以在函数返回类型中使用函数参数相关的信息。在**authAndAccess**函数中,我们指定返回类型使用c和i。如果我们按照传统语法把函数返回类型放在函数名称之前, c和i就未被声明所以不能使用。 -在这种声明中,**authAndAccess**函数返回**operator[]**应用到容器中返回的对象的类型,这也正是我们期望的结果。 +函数名称前面的**auto**不会做任何的类型推导工作。相反的,他只是暗示使用了C++11的尾置返回类型语法,即在函数形参列表后面使用一个**->** 符号指出函数的返回类型,尾置返回类型的好处是我们可以在函数返回类型中使用函数参数相关的信息。在**authAndAccess**函数中,我们指定返回类型使用c和i。如果我们按照传统语法把函数返回类型放在函数名称之前, c和i就未被声明所以不能使用。 + +在这种声明中,**authAndAccess**函数返回**operator[]** 应用到容器中返回的对象的类型,这也正是我们期望的结果。 C++11允许自动推导单一语句的lambda表达式的返回类型, C++14扩展到允许自动推导所有的lambda表达式和函数,甚至它们内含多条语句。对于**authAndAccess**来说这意味着在C++14标准下我们可以忽略尾置返回类型,只留下一个**auto**。在这种形式下**auto**不再进行auto类型推导,取而代之的是它意味着编译器将会从函数实现中推导出函数的返回类型。 ````cpp @@ -54,7 +55,7 @@ auto authAndAccess(Container& c,Index i) return c[i]; } ```` -Item2解释了函数返回类型中使用**auto**编译器实际上是使用的模板类型推导的那套规则。如果那样的话就会这里就会有一些问题,正如我们之前讨论的,**operator[]**对于大多数T类型的容器会返回一个**T&**,但是Item1解释了在模板类型推导期间,如果表达式是一个引用那么引用会被忽略。基于这样的规则,考虑它会对下面用户的代码有哪些影响: +Item2解释了函数返回类型中使用**auto**编译器实际上是使用的模板类型推导的那套规则。如果那样的话就会这里就会有一些问题,正如我们之前讨论的,**operator[]** 对于大多数T类型的容器会返回一个**T&** ,但是Item1解释了在模板类型推导期间,如果表达式是一个引用那么引用会被忽略。基于这样的规则,考虑它会对下面用户的代码有哪些影响: ````cpp std::deque d; ... @@ -62,9 +63,9 @@ authAndAccess(d,5)=10; //认证用户,返回d[5], //然后把10赋值给它 //无法通过编译器! ```` -在这里**d[5]**本该返回一个**int&**,但是模板类型推导会剥去引用的部分,因此产生了**int**返回类型。函数返回的值是一个右值,,上面的代码尝试把10赋值给右值,C++11禁止这样做,所以代码无法编译。 +在这里**d[5]**本该返回一个**int&** ,但是模板类型推导会剥去引用的部分,因此产生了**int**返回类型。函数返回的值是一个右值,,上面的代码尝试把10赋值给右值,C++11禁止这样做,所以代码无法编译。 -要想让**authAndAccess**像我们期待的那样工作,我们需要使用**decltype**类型推导来推导它的返回值,比如指定**authAndAccess**应该返回一个和**c[i]**表达式类型一样的类型。C++期望在某些情况下当类型被暗示时需要使用**decltype**类型推导的规则,C++14通过使用**decltype(auto)**说明符使得这成为可能。我们第一次看见**decltype(auto)**可能觉得非常的矛盾,(到底是decltype还是auto?),实际上我们可以这样解释它的意义:**auto**说明符表示这个类型将会被推导,**decltype**说明**decltype**的规则将会引用到这个推导过程中。因此我们可以这样写**authAndAccess**: +要想让**authAndAccess**像我们期待的那样工作,我们需要使用**decltype**类型推导来推导它的返回值,比如指定**authAndAccess**应该返回一个和**c[i]** 表达式类型一样的类型。C++期望在某些情况下当类型被暗示时需要使用**decltype**类型推导的规则,C++14通过使用**decltype(auto)** 说明符使得这成为可能。我们第一次看见**decltype(auto)** 可能觉得非常的矛盾,(到底是decltype还是auto?),实际上我们可以这样解释它的意义:**auto**说明符表示这个类型将会被推导,**decltype**说明**decltype**的规则将会引用到这个推导过程中。因此我们可以这样写**authAndAccess**: ````cpp template decltype(auto) @@ -74,10 +75,10 @@ authAndAccess(Container& c,Index i) return c[i]; } ```` -现在authAndAccess将会真正的返回c[i]的类型。现在事情解决了,一般情况下c[i]返回**T&**,authAndAccess也会返回 +现在authAndAccess将会真正的返回c[i]的类型。现在事情解决了,一般情况下c[i]返回**T&** ,authAndAccess也会返回 **T&**,特殊情况下c[i]返回一个对象,authAndAccess也会返回一个对象。 -**decltype(auto)**的使用不仅仅局限于函数返回类型,当你想对初始化表达式使用decltype推导的规则,你也可以使用: +**decltype(auto)** 的使用不仅仅局限于函数返回类型,当你想对初始化表达式使用decltype推导的规则,你也可以使用: ````cpp Widget w; @@ -143,9 +144,9 @@ authAndAccess(Container&& c,Index i) ````cpp int x =0; ```` -中,x是一个变量的名字,所以**decltype(x)**是**int**。但是如果用一个小括号包覆这个名字,比如这样**(x)**,就会产生一个比名字更复杂的表达式。对于名字来说,**x**是一个左值,C++11定义了表达式**(x)**也是一个左值。因此**decltype((x))**是**int&**。用小括号覆盖一个名字可以改变decltype对于名字产生的结果。 +中,x是一个变量的名字,所以**decltype(x)** 是**int**。但是如果用一个小括号包覆这个名字,比如这样**(x)** ,就会产生一个比名字更复杂的表达式。对于名字来说,**x**是一个左值,C++11定义了表达式**(x)** 也是一个左值。因此**decltype((x))** 是**int&** 。用小括号覆盖一个名字可以改变decltype对于名字产生的结果。 -在C++11中这稍微有点奇怪,但是由于C++14允许了**decltype(auto)**的使用,这意味着你在函数返回语句中细微的改变就可以影响类型的推导: +在C++11中这稍微有点奇怪,但是由于C++14允许了**decltype(auto)** 的使用,这意味着你在函数返回语句中细微的改变就可以影响类型的推导: ````cpp decltype(auto) f1() { @@ -162,7 +163,7 @@ decltype(auto) f2() ```` 注意不仅f2的返回类型不同于f1,而且它还引用了一个局部变量!这样的代码将会把你送上未定义行为的特快列车,一辆你绝对不想上第二次的车。 -当使用**decltype(auto)**的时候一定要加倍的小心,在表达式中看起来无足轻重的细节将会影响到类型的推导。为了确认类型推导是否产出了你想要的结果,请参见Item4描述的那些技术。 +当使用**decltype(auto)** 的时候一定要加倍的小心,在表达式中看起来无足轻重的细节将会影响到类型的推导。为了确认类型推导是否产出了你想要的结果,请参见Item4描述的那些技术。 同时你也不应该忽略decltype这块大蛋糕。没错,decltype可能会偶尔产生一些令人惊讶的结果,但那毕竟是少数情况。通常,decltype都会产生你想要的结果,尤其是当你对一个名字使用decltype时,因为在这种情况下,decltype只是做一件本分之事:它产出名字的声明类型。 @@ -170,5 +171,5 @@ decltype(auto) f2() + **decltype**总是不加修改的产生变量或者表达式的类型。 + 对于T类型的左值表达式,**decltype**总是产出T的引用即**T&**。 -+ C++14支持**decltype(auto)**,就像auto一样,推导出类型,但是它使用自己的独特规则进行推导。 ++ C++14支持**decltype(auto)** ,就像auto一样,推导出类型,但是它使用自己的独特规则进行推导。