diff --git a/5.RRefMovSemPerfForw/item26.md b/5.RRefMovSemPerfForw/item26.md index 617ff0b..0dd4b01 100644 --- a/5.RRefMovSemPerfForw/item26.md +++ b/5.RRefMovSemPerfForw/item26.md @@ -26,9 +26,9 @@ logAndAdd("Patty Dog"); //传递字符串字面值 在第一个调用中,`logAndAdd`的形参`name`绑定到变量`petName`。在`logAndAdd`中`name`最终传给`names.emplace`。因为`name`是左值,会拷贝到`names`中。没有方法避免拷贝,因为是左值(`petName`)传递给`logAndAdd`的。 -在第二个调用中,形参`name`绑定到右值(显式从“`Persephone`”创建的临时`std::string`)。`name`本身是个左值,所以它被拷贝到`names`中,但是我们意识到,原则上,它的值会被移动到`names`中。本次调用中,我们有个拷贝代价,但是我们应该能用移动勉强应付。 +在第二个调用中,形参`name`绑定到右值(显式从“`Persephone`”创建的临时`std::string`)。`name`本身是个左值,所以它被拷贝到`names`中,但是我们意识到,原则上,它的值可以被移动到`names`中。本次调用中,我们有个拷贝代价,但是我们应该能用移动勉强应付。 -在第三个调用中,形参`name`也绑定一个右值,但是这次是通过“`Patty Dog`”隐式创建的临时`std::string`变量。就像第二个调用中,`name`被拷贝到`names`,但是这里,传递给`logAndAdd`的实参是一个字符串字面量。直接将字符串字面量传递给`emplace`,不会创建`std::string`的临时变量,而是直接在`std::multiset`中通过字面量构建`std::string`。在第三个调用中,我们有个`std::string`拷贝开销,但是我们连移动开销都不想要,更别说拷贝的。 +在第三个调用中,形参`name`也绑定一个右值,但是这次是通过“`Patty Dog`”隐式创建的临时`std::string`变量。就像第二个调用中,`name`被拷贝到`names`,但是这里,传递给`logAndAdd`的实参是一个字符串字面量。如果直接将字符串字面量传递给`emplace`,就不会创建`std::string`的临时变量,而是直接在`std::multiset`中通过字面量构建`std::string`。在第三个调用中,我们有个`std::string`拷贝开销,但是我们连移动开销都不想要,更别说拷贝的。 我们可以通过使用通用引用(参见[Item24](https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/5.RRefMovSemPerfForw/item24.md))重写`logAndAdd`来使第二个和第三个调用效率提升,按照[Item25](https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/5.RRefMovSemPerfForw/item25.md)的说法,`std::forward`转发这个引用到`emplace`。代码如下: @@ -137,7 +137,7 @@ auto cloneOfP(p); //从p创建新Person;这通不过编译 “为什么?”你可能会疑问,“为什么拷贝构造会被完美转发构造替代?我们显然想拷贝`Person`到另一个`Person`”。确实我们是这样想的,但是编译器严格遵循C++的规则,这里的相关规则就是控制对重载函数调用的解析规则。 -编译器的理由如下:`cloneOfP`被non-`const`左值`p`初始化,这意味着可以模板化构造函数可被实例化为采用`Person`类型的non-`const`左值。实例化之后,`Person`类看起来是这样的: +编译器的理由如下:`cloneOfP`被non-`const`左值`p`初始化,这意味着模板化构造函数可被实例化为采用`Person`类型的non-`const`左值。实例化之后,`Person`类看起来是这样的: ```cpp class Person { @@ -145,8 +145,9 @@ public: explicit Person(Person& n) //由完美转发模板初始化 : name(std::forward(n)) {} - explicit Person(int idx); //同之前一样,拷贝构造函数 - Person(const Person& rhs); //(编译器生成的) + explicit Person(int idx); //同之前一样 + + Person(const Person& rhs); //拷贝构造函数(编译器生成的) … }; ``` @@ -180,7 +181,7 @@ public: 但是没啥影响,因为重载规则规定当模板实例化函数和非模板函数(或者称为“正常”函数)匹配优先级相当时,优先使用“正常”函数。拷贝构造函数(正常函数)因此胜过具有相同签名的模板实例化函数。 -(如果你想知道为什么编译器在生成一个拷贝构造函数时还会模板实例化一个相同签名的函数,参考[Item17](https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/3.MovingToModernCpp/item17.md)) +(如果你想知道为什么编译器在生成一个拷贝构造函数时还会模板实例化一个相同签名的函数,参考[Item17](https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/3.MovingToModernCpp/item17.md)。) 当继承纳入考虑范围时,完美转发的构造函数与编译器生成的拷贝、移动操作之间的交互会更加复杂。尤其是,派生类的拷贝和移动操作的传统实现会表现得非常奇怪。来看一下: