vs.push_back(queenOfDisco); //拷贝构造queenOfDisco
vs.emplace_back(queenOfDisco); //同上
如果你拥有一个容器,例如放着std::string
,那么当你通过插入(insertion)函数(例如insert
,push_front
,push_back
,或者对于std::forward_list
来说是insert_after
)添加新元素时,你传入的元素类型应该是std::string
。毕竟,这就是容器里的内容。
逻辑上看来如此,但是并非总是如此。考虑如下代码:
std::vector<std::string> vs; //std::string的容器
vs.push_back("xyzzy"); //添加字符串字面量
这里,容器里内容是std::string
,但是你有的——你实际上试图通过push_back
加入的——是字符串字面量,即引号内的字符序列。字符串字面量并不是std::string
,这意味着你传递给push_back
的实参并不是容器里的内容类型。
std::vector
的push_back
被按左值和右值分别重载:
template <class T, //来自C++11标准
class Allocator = allocator<T>>
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
运行时发生了什么:
- 一个
std::string
的临时对象从字面量“xyzzy
”被创建。这个对象没有名字,我们可以称为temp
。temp
的构造是第一次std::string
构造。因为是临时变量,所以temp
是右值。 temp
被传递给push_back
的右值重载函数,绑定到右值引用形参x
。在std::vector
的内存中一个x
的副本被创建。这次构造——也是第二次构造——在std::vector
内部真正创建一个对象。(将x
副本拷贝到std::vector
内部的构造函数是移动构造函数,因为x
在它被拷贝前被转换为一个右值,成为右值引用。有关将右值引用形参强制转换为右值的信息,请参见条款25)。- 在
push_back
返回之后,temp
立刻被销毁,调用了一次std::string
的析构函数。
对于性能执着的人不禁注意到是否存在一种方法可以获取字符串字面量并将其直接传入到步骤2里在std::vector
内构造std::string
的代码中,可以避免临时对象temp
的创建与销毁。这样的效率最好,对于性能执着的人也不会有什么意见了。
因为你是一个C++开发者,所以你更大可能是一个对性能执着的人。如果你不是C++开发者,你可能也会同意这个观点。(如果你根本不考虑性能,为什么你没在用Python?)所以我很高兴告诉你有一种方法,恰是在调用push_back
中实现效率最大化。它不叫push_back
。push_back
函数不正确。你需要的是emplace_back
。
emplace_back
就是像我们想要的那样做的:使用传递给它的任何实参直接在std