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。