跳到主要內容

C++17 新功能 try_emplace

C++17 新功能 try_emplace

回顧 emplace

大家的好朋友 Standard Template Library (STL) 容器提供如 push_back, insert 等介面,讓我們塞東西進去; C++11 之後,新增了 emplace 系列的介面,如 std::vector::emplace_back, std::map::emplace 等,差異在於 emplace 是在容器內 in-place 直接建構新元素,而不像 push_back 在傳遞參數前建構,下面用實例來說明:

struct Value {
    // ctor1
    Value(int size) : array(new char[size]), size(size) {
	  printf("ctor1: %d\n", size);
	}
	// ctor2
	Value(const Value& v) : array(new char[v.size]), size(v.size) {
	  printf("ctor2: %d\n", size);
	  memcpy(array.get(), v.array.get(), size);
	}
  private:
	std::unique_ptr<char[]> array;
	int size = 0;
};

struct Value 定義了自訂建構子 (ctor1),以指定大小 size 配置陣列,複製建構子 (ctor2) 則會配置與來源相同大小及內容的陣列,為了方便觀察加了一些 printf。當我們如下使用 std::vector::push_back

std::vector<Value> v;
v.push_back(Value(2048));

首先 Value 會先呼叫 ctor1,傳給 push_back 之後再用來初始化 (呼叫 ctor2) 容器內的新元素,所以印出來的資訊是:

ctor1: 2048
ctor2: 2048

使用 emplace_back 的版本:

v.emplace_back(2048);

可以發現不需要加上 Value() 的建構了,emplace_back 會將參數 (2048) 當成新元素的初始化參數直接在容器內建構,這時印出來的資訊會是

ctor1: 2048

附帶一提,若讓 Value 支援移動建構式 (move constructor) 例如:

Value(Value &&v)
  : array(std::move(v.array)), size(std::move(v.size)) {
  printf("ctor3: no copy!\n");
}

push_back 版本仍需呼叫兩種建構式 (ctor1, ctor3),但可免除配置陣列與複製內容的運算。

為何需要 try_emplace

std::map 或是 std::unordered_map 都須保證鍵值 (key) 的唯一性,當插入新元素遇到重複鍵值時,該操作會失敗,呼叫者可透過回傳值來判斷操作成功與否,然而遇到 N42701 提出的例子時,程式碼就變得較不直覺

std::map<std::string, std::unique_ptr<Foo>> m;
m["foo"] = nullptr;

auto ptr = std::make_unique_ptr<Foo>;
auto res = m.emplace("foo", std::move(ptr));
assert(ptr); // ???

由於 emplace 會失敗,然而 C++11 中並未定義新元素的建構時機 (p746, $23.2.4, N63902),也就是看 STL 實作的方式,即使遇到 emplace 失敗,新元素可能已經被建立再銷毀了;以前面的例子來說,ptr 可能已經被移動到新元素中跟著被釋放掉了(註)。為了避免這個問題,C++11 時我們得先用 find 確認鍵值是否存在,再進行 emplace 操作;因此,C++17 納入了 try_emplace ,此介面保證在插入失敗時,不會偷取建構參數。

註:std::move(ptr) 是強制把 ptr 當成右值,如果沒有被用來建構新元素,並不會真的被移動。


  1. N4270: Improved insertion interface for unique-key maps ↩︎

  2. N3690: ISO/IEC JTC1 SC22 WG21 N 3690 ↩︎

留言

這個網誌中的熱門文章

得利油漆色卡編碼方式

得利油漆色卡編碼方式 類似 Munsell 色彩系統 ,編碼方式為 HUE LRV/CHROMA 例如 10GY 61/449 ( 色卡 ) 編碼數值 描述 10GY hue ,色輪上從 Y(ellow) 到 G(reen) 區分為 0 ~ 99 ,數值越小越靠近 Y,越大越靠近 G 61 LRV (Light Reflectance Value) 塗料反射光源的比率,數值從 0% ~ 100% ,越高越亮,反之越暗,也可理解為明度 449 chroma 可理解為彩度,數值沒有上限,越高顏色純度 (濃度) 越高 取決於測量儀器,對應至 RGB 並不保證視覺感受相同。 參考資料: 色卡對照網站 e-paint.co.uk Written with StackEdit .

UTF8 與 Unicode 的轉換 (C++)

UTF8 與 Unicode 的轉換 (C++) 先釐清一下這兩者的性質 Unicode: 為世界上所有的文字系統制訂的標準,基本上就是給每個字(letter)一個編號 UTF-8: 為 unicode 的編號制定一個數位編碼方法 UTF-8 是一個長度介於 1~6 byte 的編碼,將 unicode 編號 (code point) 分為六個區間如下表 1 Bits First code point Last code point Bytes Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 7 U+0000 U+007F 1 0xxxxxxx 11 U+0080 U+07FF 2 110xxxxx 10xxxxxx 16 U+0800 U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx 21 U+10000 U+1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 26 U+200000 U+3FFFFFF 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 31 U+4000000 U+7FFFFFFF 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 觀察上面的表應該可以發現 除了 7 bits 的區間外,第一個 byte 開頭連續 1 的個數就是長度,例如 110XXXXX 就是 2 byte 長,而 1110xxxx 就是 3 byte 除了第一個 byte 外,之後的 byte 前兩個 bit 一定是 10 開頭,這樣的好處在於確立了編碼的 self-synchronizeing,意即當編碼為多個 byte 時,任取一個 byte 無法正常解碼。 Note 第一點中的例外 (7 bits) 是為了與 ASCII 的相容性,而第二點會影響到 code point 至 UTF-8 的轉換。 為了與 UTF-16 的相容性,在 R