mirror of
https://github.com/CnTransGroup/EffectiveModernCppChinese.git
synced 2025-01-26 11:50:46 +08:00
Update item30.md
This commit is contained in:
parent
fd71cc7b83
commit
cae792278d
@ -63,7 +63,7 @@ fwd({ 1, 2, 3 }); //错误!不能编译
|
||||
|
||||
所有这种错误有相同的原因。在对`f`的直接调用(例如`f({ 1, 2, 3 })`),编译器看看调用地传入的实参,看看`f`声明的形参类型。它们把调用地的实参和声明的实参进行比较,看看是否匹配,并且必要时执行隐式转换操作使得调用成功。在上面的例子中,从`{ 1, 2, 3 }`生成了临时`std::vector<int>`对象,因此`f`的形参`v`会绑定到`std::vector<int>`对象上。
|
||||
|
||||
当通过调用函数模板`fwd`间接调用`f`时,编译器不再把调用地传入给`fwd`的实参和`f`的声明中形参类型进行比较。而是**推导**传入给`fwd`的实参类型,然后比较推导后的参数类型和`f`的形参声明类型。当下面情况任何一个发生时,完美转发就会失败:
|
||||
当通过调用函数模板`fwd`间接调用`f`时,编译器不再把调用地传入给`fwd`的实参和`f`的声明中形参类型进行比较。而是**推导**传入给`fwd`的实参类型,然后比较推导后的实参类型和`f`的形参声明类型。当下面情况任何一个发生时,完美转发就会失败:
|
||||
|
||||
- **编译器不能推导出`fwd`的一个或者多个形参类型。**这种情况下代码无法编译。
|
||||
- **编译器推导“错”了`fwd`的一个或者多个形参类型。**在这里,“错误”可能意味着`fwd`的实例将无法使用推导出的类型进行编译,但是也可能意味着使用`fwd`的推导类型调用`f`,与用传给`fwd`的实参直接调用`f`表现出不一致的行为。这种不同行为的原因可能是因为`f`是个重载函数的名字,并且由于是“不正确的”类型推导,在`fwd`内部调用的`f`重载和直接调用的`f`重载不一样。
|
||||
@ -119,7 +119,7 @@ fwd(Widget::MinVals); //错误!不应该链接
|
||||
|
||||
代码可以编译,但是不应该链接。如果这让你想到使用`MinVals`地址会发生的事,确实,底层的问题是一样的。
|
||||
|
||||
尽管代码中没有使用`MinVals`的地址,但是`fwd`的参数是通用引用,而引用,在编译器生成的代码中,通常被视作指针。在程序的二进制底层代码中(以及硬件中)指针和引用是一样的。在这个水平上,引用只是可以自动解引用的指针。在这种情况下,通过引用传递`MinVals`实际上与通过指针传递`MinVals`是一样的,因此,必须有内存使得指针可以指向。通过引用传递的整型`static const`数据成员,通常需要定义它们,这个要求可能会造成在不使用完美转发的代码成功的地方,使用等效的完美转发失败。(译者注:这里意思应该是没有定义,完美转发就会失败)
|
||||
尽管代码中没有使用`MinVals`的地址,但是`fwd`的形参是通用引用,而引用,在编译器生成的代码中,通常被视作指针。在程序的二进制底层代码中(以及硬件中)指针和引用是一样的。在这个水平上,引用只是可以自动解引用的指针。在这种情况下,通过引用传递`MinVals`实际上与通过指针传递`MinVals`是一样的,因此,必须有内存使得指针可以指向。通过引用传递的整型`static const`数据成员,通常需要定义它们,这个要求可能会造成在不使用完美转发的代码成功的地方,使用等效的完美转发失败。(译者注:这里意思应该是没有定义,完美转发就会失败)
|
||||
|
||||
可能你也注意到了在上述讨论中我使用了一些模棱两可的词。代码“不应该”链接。引用“通常”被看做指针。传递整型`static const`数据成员“通常”要求定义。看起来就像有些事情我没有告诉你......
|
||||
|
||||
@ -158,7 +158,7 @@ int processVal(int value, int priority);
|
||||
f(processVal); //可以
|
||||
```
|
||||
|
||||
但是我们会发现一些吃惊的事情。`f`要求一个函数指针作为实参,但是`processVal`不是一个函数指针或者一个函数,它是同名的两个不同函数。但是,编译器可以知道它需要哪个:匹配上`f`的形参类型的那个。因此选择了一个`int`参数的`processVal`地址传递给`f`。
|
||||
但是我们会发现一些吃惊的事情。`f`要求一个函数指针作为实参,但是`processVal`不是一个函数指针或者一个函数,它是同名的两个不同函数。但是,编译器可以知道它需要哪个:匹配上`f`的形参类型的那个。因此选择了仅带有一个`int`的`processVal`地址传递给`f`。
|
||||
|
||||
工作的基本机制是`f`的声明让编译器识别出哪个是需要的`processVal`。但是,`fwd`是一个函数模板,没有它可接受的类型的信息,使得编译器不可能决定出哪个函数应被传递:
|
||||
|
||||
@ -207,7 +207,7 @@ struct IPv4Header {
|
||||
};
|
||||
```
|
||||
|
||||
如果声明我们的函数`f`(转发函数`fwd`的目标)为接收一个`std::size_t`的参数,则使用`IPv4Header`对象的`totalLength`字段进行调用没有问题:
|
||||
如果声明我们的函数`f`(转发函数`fwd`的目标)为接收一个`std::size_t`的形参,则使用`IPv4Header`对象的`totalLength`字段进行调用没有问题:
|
||||
|
||||
```cpp
|
||||
void f(std::size_t sz); //要调用的函数
|
||||
|
Loading…
Reference in New Issue
Block a user