C++11中大部分的容器对于添加元素除了传统的 insert 或者 pusb_back/push_front 以外都提供一个新的函数叫作 emplace。 好比若是你想要向 std::vector 的末尾添加一个数据,你能够:函数
std::vector<int> nums;
nums.push_back(1);
你也可使用:设计
std::vector<int> nums;
nums.empace_back(1);
避免没必要要的临时对象的产生
emplace 最大的做用是避免产生没必要要的临时变量,由于它能够完成 in place 的构造,举个例子:对象
struct Foo {
Foo(int n, double x);
};ci
std::vector<Foo> v;
v.emplace(someIterator, 42, 3.1416); // 没有临时变量产生
v.insert(someIterator, Foo(42, 3.1416)); // 须要产生一个临时变量
v.insert(someIterator, {42, 3.1416}); // 须要产生一个临时变量
这是 emplaceemplace 和 insertinsert 最大的区别点。emplaceemplace 的语法看起来难以想象,在上 面的例子中后面两个参数自动用来构造 vector 内部的 Foo 对象。作到这一点主要 使用了 C++11 的两个新特性 变参模板变参模板 和 完美转发完美转发。”变参模板”使得 emplace 能够接受任意参数,这样就能够适用于任意对象的构建。
”完美转发”使得接收下来的参数 可以原样的传递给对象的构造函数,这带来另外一个方便性就是即便是构造函数声明为 explicitexplicit 它仍是能够正常工做,由于它不存在临时变量和隐式转换。string
struct Bar {
Bar(int a) {}
explicit Bar(int a, double b) {}
};it
int main(void)
{
vector<Bar> bv;
bv.push_back(1); // 隐式转换生成临时变量
bv.push_back(Bar(1)); // 显示构造临时变量
bv.emplace_back(1); // 没有临时变量模板
//bv.push_back({1, 2.0}); // 没法进行隐式转换
bv.push_back(Bar(1, 2.0)); // 显示构造临时变量
bv.emplace_back(1, 2.0); // 没有临时变量容器
return 0;
}
map 的特殊状况
mapmap 类型的 emplaceemplace 处理比较特殊,由于和其余的容器不一样,map 的 emplace 函数把它接收到的全部的参数都转发给 pairpair 的构造函数。对于一个 pairpair 来讲,它既须要构造它的 keykey 又须要构造它的 valuevalue。若是咱们按照普通的 的语法使用变参模板,咱们没法区分哪些参数用来构造 keykey, 哪些用来构造 valuevalue。 好比下面的代码:基础
map<string, complex<double>> scp;
scp.emplace("hello", 1, 2); // 没法区分哪一个参数用来构造 key 哪些用来构造 value
// string s("hello", 1), complex<double> cpx(2) ???
// string s("hello"), complex<double> cpx(1, 2) ???
因此咱们须要一种方式既能够接受异构变长参数,又能够区分 key 和 value,解决 方式是使用 C++11 中提供的 tuple。变量
pair<string, complex<double>> scp(make_tuple("hello"), make_tuple(1, 2));
而后这种方式是有问题的,由于这里有歧义,第一个 tuple 会被当成是 key,第二 个tuple会被当成 value。最终的结果是类型不匹配而致使对象建立失败,为了解决 这个问题,C++11 设计了 piecewise_construct_t 这个类型用于解决这种歧义,它 是一个空类,存在的惟一目的就是解决这种歧义,全局变量 std::piecewise_construct 就是该类型的一个变量。因此最终的解决方式以下:
pair<string, complex<double>> scp(piecewise_construct, make_tuple("hello"), make_tuple(1, 2));
固然由于 map 的 emplace 把参数原样转发给 pair 的构造,因此你须要使用一样 的语法来完成 emplace 的调用,固然你可使用 forward_as_tuple 替代 make_tuple,该函数会帮你构造一个 tuple 并转发给 pair 构造。
map<string, complex<double>> scp;
scp.emplace(piecewise_construct,
forward_as_tuple("hello"),
forward_as_tuple(1, 2));
因此对于 map 来讲你虽然避免了临时变量的构造,可是你却须要构建两个 tuple 。 这种 traedoff 是否值得须要代码编写者本身考虑,从方便性和代码优雅性上来讲:
scp.insert({"world", {1, 2}});这种写法都要赛过前面这个 emplace 版本。因此我的认为对于临时变量构建代价不是 很大的对象(好比基础类型)推荐使用 insert 而不是 emplace。