条款42:考虑使用置入代替插入

vs.push_back(queenOfDisco);         //拷贝构造queenOfDisco
vs.emplace_back(queenOfDisco);      //同上

如果你拥有一个容器,例如放着std::string,那么当你通过插入(insertion)函数(例如insertpush_frontpush_back,或者对于std::forward_list来说是insert_after)添加新元素时,你传入的元素类型应该是std::string。毕竟,这就是容器里的内容。

逻辑上看来如此,但是并非总是如此。考虑如下代码:

std::vector vs;        //std::string的容器
vs.push_back("xyzzy");              //添加字符串字面量

这里,容器里内容是std::string,但是你有的——你实际上试图通过push_back加入的——是字符串字面量,即引号内的字符序列。字符串字面量并不是std::string,这意味着你传递给push_back的实参并不是容器里的内容类型。

std::vectorpush_back被按左值和右值分别重载:

template >
class vector {
public:
    …
    void push_back(const T& x);     //插入左值
    void push_back(T&& x);          //插入右值
    …
};

这个调用中,编译器看到实参类型(const char[6])和push_back采用的形参类型(std::string的引用)之间不匹配。它们通过从字符串字面量创建一个std::string类型的临时对象来消除不匹配,然后传递临时变量给push_back。换句话说,编译器处理的这个调用应该像这样:

vs.push_back(std::string("xyzzy")); //创建临时std::string,把它传给push_back

代码可以编译并运行,皆大欢喜。除了对于性能执着的人意识到了这份代码不如预期的执行效率高。

为了在std::string容器中创建新元素,调用了std::string的构造函数,但是这份代码并不仅调用了一次构造函数,而是调用了两次,而且还调用了std::string析构函数。下面是在push_back运行时发生了什么:

  1. 一个std::string的临时对象从字面量“xyzzy”被创建。这个对象没有名字,我们可以称为temptemp的构造是第一次std::string构造。因为是临时变量,所以temp是右值。
  2. temp被传递给push_back的右值重载函数,绑定到右值引用形参x。在std::vector的内存中一个x的副本被创建。这次构造——也是第二次构造——在std::vector内部真正创建一个对象。(将x副本拷贝到std::vector内部的构造函数是移动构造函数,因为x在它被拷贝前被转换为一个右值,成为右值引用。有关将右值引用形参强制转换为右值的信息,请参见条款25)。
  3. push_back返回之后,temp立刻被销毁,调用了一次std::string的析构函数。

对于性能执着的人不禁注意到是否存在一种方法可以获取字符串字面量并将其直接传入到步骤2里在std::vector内构造std::string的代码中,可以避免临时对象temp的创建与销毁。这样的效率最好,对于性能执着的人也不会有什么意见了。

因为你是一个C++开发者,所以你更大可能是一个对性能执着的人。如果你不是C++开发者,你可能也会同意这个观点。(如果你根本不考虑性能,为什么你没在用Python?)所以我很高兴告诉你有一种方法,恰是在调用push_back中实现效率最大化。它不叫push_backpush_back函数不正确。你需要的是emplace_back

emplace_back就是像我们想要的那样做的:使用传递给它的任何实参直接在std

你可能感兴趣的:(Effective,Modern,C++,c++,开发语言)