修改部分翻译

This commit is contained in:
猫耳堀川雷鼓 2021-01-30 21:48:08 +08:00 committed by GitHub
parent 2324cdfcfd
commit 950c5031ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -24,17 +24,17 @@ void f(ParamType param);
````
它的调用看起来像这样
````cpp
f(expr); //使用表达式调用f
f(expr); //使用表达式调用f
````
在编译期间,编译器使用`expr`进行两个类型推导:一个是针对`T`的,另一个是针对`ParamType`的。这两个类型通常是不同的,因为`ParamType`包含一些修饰,比如`const`和引用修饰符。举个例子,如果模板这样声明:
````cpp
template<typename T>
void f(const T& param); //ParamType是const T&
void f(const T& param); //ParamType是const T&
````
然后这样进行调用
````cpp
int x = 0;
f(x); //用一个int类型的变量调用f
f(x); //用一个int类型的变量调用f
````
`T`被推导为`int``ParamType`却被推导为`const int&`
@ -49,7 +49,7 @@ f(x); //用一个int类型的变量调用f
template<typename T>
void f(ParamType param);
f(expr); //从expr中推导T和ParamType
f(expr); //从expr中推导T和ParamType
````
<font size = 5>**情景一:`ParamType`是一个指针或引用,但不是通用引用**</font>
@ -66,15 +66,15 @@ void f(T& param); //param是一个引用
````
我们声明这些变量,
````cpp
int x=27; //x是int
int x=27; //x是int
const int cx=x; //cx是const int
const int& rx=x; //rx是指向作为const int的x的引用
````
在不同的调用中,对`param`和`T`推导的类型会是这样:
````cpp
f(x); //T是intparam的类型是int&
f(cx); //T是const intparam的类型是const int&
f(rx); //T是const intparam的类型是const int&
f(x); //T是intparam的类型是int&
f(cx); //T是const intparam的类型是const int&
f(rx); //T是const intparam的类型是const int&
````
在第二个和第三个调用中,注意因为`cx`和`rx`被指定为`const`值,所以`T`被推导为`const int`,从而产生了`const int&`的形参类型。这对于调用者来说很重要。当他们传递一个`const`对象给一个引用类型的形参时他们期望对象保持不可改变性也就是说形参是reference-to-`const`的。这也是为什么将一个`const`对象传递给以`T&`类型为形参的模板安全的:对象的常量性`const`ness会被保留为`T`的一部分。
@ -86,15 +86,15 @@ f(rx); //T是const intparam的类型是const int&
```cpp
template<typename T>
void f(const T& param); //param现在是reference-to-const
void f(const T& param); //param现在是reference-to-const
int x = 27; //如之前一样
int x = 27; //如之前一样
const int cx = x; //如之前一样
const int& rx = x; //如之前一样
f(x); //T是intparam的类型是const int&
f(cx); //T是intparam的类型是const int&
f(rx); //T是intparam的类型是const int&
f(x); //T是intparam的类型是const int&
f(cx); //T是intparam的类型是const int&
f(rx); //T是intparam的类型是const int&
```
同之前一样,`rx`的reference-ness在类型推导中被忽略了。
@ -105,11 +105,11 @@ f(rx); //T是intparam的类型是const int&
template<typename T>
void f(T* param); //param现在是指针
int x = 27; //同之前一样
int x = 27; //同之前一样
const int *px = &x; //px是指向作为const int的x的指针
f(&x); //T是intparam的类型是int*
f(px); //T是const intparam的类型是const int*
f(&x); //T是intparam的类型是int*
f(px); //T是const intparam的类型是const int*
```
到现在为止你会发现你自己打哈欠犯困因为C++的类型推导规则对引用和指针形参如此自然,书面形式来看这些非常枯燥。所有事情都那么理所当然!那正是在类型推导系统中你所想要的。
@ -126,21 +126,21 @@ f(px); //T是const intparam的类型是const int*
template<typename T>
void f(T&& param); //param现在是一个通用引用类型
int x=27; //如之前一样
int x=27; //如之前一样
const int cx=x; //如之前一样
const int & rx=cx; //如之前一样
f(x); //x是左值所以T是int&
//param类型也是int&
f(x); //x是左值所以T是int&
//param类型也是int&
f(cx); //cx是左值所以T是const int&
//param类型也是const int&
f(cx); //cx是左值所以T是const int&
//param类型也是const int&
f(rx); //rx是左值所以T是const int&
//param类型也是const int&
f(rx); //rx是左值所以T是const int&
//param类型也是const int&
f(27); //27是右值所以T是int
//param类型就是int&&
f(27); //27是右值所以T是int
//param类型就是int&&
````
[Item24](https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/5.RRefMovSemPerfForw/item24.md)详细解释了为什么这些例子是像这样发生的。这里关键在于通用引用的类型推导规则是不同于普通的左值或者右值引用的。尤其是,当通用引用被使用时,类型推导会区分左值实参和右值实参,但是对非通用引用时不会区分。
@ -159,13 +159,13 @@ void f(T param); //以传值的方式处理param
因此
````cpp
int x=27; //如之前一样
int x=27; //如之前一样
const int cx=x; //如之前一样
const int & rx=cx; //如之前一样
f(x); //T和param的类型都是int
f(cx); //T和param的类型都是int
f(rx); //T和param的类型都是int
f(x); //T和param的类型都是int
f(cx); //T和param的类型都是int
f(rx); //T和param的类型都是int
````
注意即使`cx`和`rx`表示`const`值,`param`也不是`const`。这是有意义的。`param`是一个完全独立于`cx`和`rx`的对象——是`cx`或`rx`的一个拷贝。具有常量性的`cx`和`rx`不可修改并不代表`param`也是一样。这就是为什么`expr`的常量性`const`ness或易变性`volatile`ness)在推导`param`类型时会被忽略:因为`expr`不可修改并不意味着他的拷贝也不能被修改。
@ -174,10 +174,10 @@ f(rx); //T和param的类型都是int
template<typename T>
void f(T param); //仍然以传值的方式处理param
const char* const ptr = //ptr是一个常量指针指向常量对象
" Fun with pointers";
const char* const ptr = //ptr是一个常量指针指向常量对象
"Fun with pointers";
f(ptr); //传递const char * const类型的实参
f(ptr); //传递const char * const类型的实参
````
在这里,解引用符号(\*)的右边的`const`表示`ptr`本身是一个`const``ptr`不能被修改为指向其它地址也不能被设置为null解引用符号左边的`const`表示`ptr`指向一个字符串,这个字符串是`const`,因此字符串不能被修改)。当`ptr`作为实参传给`f`,组成这个指针的每一比特都被拷贝进`param`。像这种情况,`ptr`**自身的值会被传给形参**,根据类型推导的第三条规则,`ptr`自身的常量性`const`ness将会被省略所以`param`是`const char*`,也就是一个可变指针指向`const`字符串。在类型推导中,这个指针指向的数据的常量性`const`ness将会被保留但是当拷贝`ptr`来创造一个新指针`param`时,`ptr`自身的常量性`const`ness将会被忽略。
@ -197,7 +197,7 @@ const char * ptrToName = name; //数组退化为指针
template<typename T>
void f(T param); //传值形参的模板
f(name); //T和param会推导成什么类型?
f(name); //T和param会推导成什么类型?
````
我们从一个简单的例子开始,这里有一个函数的形参是数组,是的,这样的语法是合法的,
````cpp
@ -205,7 +205,7 @@ void myFunc(int param[]);
````
但是数组声明会被视作指针声明,这意味着`myFunc`的声明和下面声明是等价的:
````cpp
void myFunc(int* param); //与上面相同的函数
void myFunc(int* param); //与上面相同的函数
````
数组与指针形参这样的等价是C语言的产物C++又是建立在C语言的基础上它让人产生了一种数组和指针是等价的的错觉。
@ -220,7 +220,7 @@ void f(T& param); //传引用形参的模板
````
我们这样进行调用,
````cpp
f(name); //传数组给f
f(name); //传数组给f
````
`T`被推导为了真正的数组!这个类型包括了数组的大小,在这个例子中`T`被推导为`const char[13]``param`则被推导为`const char (&)[13]`。是的,这种语法看起来简直有毒,但是知道它将会让你在关心这些问题的人的提问中获得大神的称号。
@ -229,21 +229,21 @@ f(name); //传数组给f
//在编译期间返回一个数组大小的常量值(
//数组形参没有名字,因为我们只关心数组
//的大小)
template<typename T, std::size_t N> //关于
template<typename T, std::size_t N> //关于
constexpr std::size_t arraySize(T (&)[N]) noexcept //constexpr
{ //和noexcept
return N; //的信息
} //请看下面
{ //和noexcept
return N; //的信息
} //请看下面
````
在[Item15](https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/3.MovingToModernCpp/item15.md)提到将一个函数声明为`constexpr`使得结果在编译期间可用。这使得我们可以用一个花括号声明一个数组,然后第二个数组可以使用第一个数组的大小作为它的大小,就像这样:
````cpp
int keyVals[] = {1,3,5,7,9,11,22,25}; //keyVals有七个元素
int mappedVals[arraySize(keyVals)]; //mappedVals也有七个
int mappedVals[arraySize(keyVals)]; //mappedVals也有七个
````
当然作为一个现代C++程序员,你自然应该想到使用`std::array`而不是内置的数组:
````cpp
std::array<int, arraySize(keyVals)> mappedVals; //mappedVals的size为7
std::array<int, arraySize(keyVals)> mappedVals; //mappedVals的大小为7
````
至于`arraySize`被声明为`noexcept`,会使得编译器生成更好的代码,具体的细节请参见[Item14](https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/3.MovingToModernCpp/item14.md)。
@ -252,18 +252,18 @@ std::array<int, arraySize(keyVals)> mappedVals; //mappedVals的size为7
在C++中不止是数组会退化为指针,函数类型也会退化为一个函数指针,我们对于数组类型推导的全部讨论都可以应用到函数类型推导和退化为函数指针上来。结果是:
````cpp
void someFunc(int, double); //someFunc是一个函数
//类型是void(int,double)
//类型是void(int,double)
template<typename T>
void f1(T param); //传值给f1
void f1(T param); //传值给f1
template<typename T>
void f2(T & param); //传引用给f2
void f2(T & param); //传引用给f2
f1(someFunc); //param被推导为指向函数的指针
//类型是void(*)(int, double)
f2(someFunc); //param被推导为指向函数的引用
//类型是void(&)(int, double)
f1(someFunc); //param被推导为指向函数的指针
//类型是void(*)(int, double)
f2(someFunc); //param被推导为指向函数的引用
//类型是void(&)(int, double)
````
这个实际上没有什么不同,但是如果你知道数组退化为指针,你也会知道函数退化为指针。