2018年8月7日 星期二

C++11 rvalue 使用筆記

C++11 中多了一個叫 rvalue 的東西表示「可以破壞的暫時變數」
當這些東西跟 STL, return value optimization (RVO) 等等的東西融合時
就讓事情變得很複雜了,讓我現在都不敢跟別人說我會 C++ 了
因此本文紀錄了一些我遇到的 case,希望未來也會繼續更新

把資料 move+insert 到 map 裡面

目的:比較 make_tuple vs forward_as_tuple 以及是否使用 move
並盡量減少 move, copy 的數量?
感謝強者我同學小新提供這個討論
#include <tuple>
#include <iotsream>
using namespace std;

struct A {
  A() { cout << "Ctor" << endl; }
  A(const A&) { cout << "Copy" << endl; }
  A(A&&) { cout << "Move" << endl; }
};

int main() {
  A a;
  map<int,tuple<A>> m;
  cout << "1" << endl;
  m.insert(make_pair(1, make_tuple(a)));
  cout << "2" << endl;
  m.insert(make_pair(2, make_tuple(move(a))));
  cout << "3" << endl;
  m.insert(make_pair(3, forward_as_tuple(a)));
  cout << "4" << endl;
  m.insert(make_pair(4, forward_as_tuple(move(a))));
  cout << "5" << endl;
  m.emplace(5, make_tuple(move(a)));
  cout << "6" << endl;
  m.emplace(6, forward_as_tuple(move(a)));
  return 0;
}
實際執行結果如下 gcc version 8.1.1 20180531 (GCC)
Ctor
1
Copy
Move
Move
2
Move
Move
Move
3
Copy
4
Move
5
Move
Move
6
Move

  1. make_tuple(a) copy 了一次,之後 make_pair, insert 各自 move 了一次。因為都是 rvalue。這邊的 make_pair 等效於 pair<int, tuple<A>>(...),所以需要 move。
  2. 只是把 1 的第一個 copy 改成 move。
  3. forward_as_tuple(a) 等效於 make_tuple<A&>(a),之後 make_pair 是 pair<int, tuple<A&>> 也是 reference type,所以不用 move/copy,也就是 copy 是在 insert 發生的。
  4. forward_as_tuple(move(a)) 等效於 make_tuple<A&&>(move(a)),跟 3 類似,move 也是在 insert 發生的。
  5. 使用了 C++11 的 emplace,跟 2 一樣,但是省去了 make_pair 的 overhead。
  6. 應該是成本最低的方法,跟 4 一樣,但是省去了 make_pair 的 overhead。
因為 a 是 lvalue 的關係,使用 move() 才有機會避免 copy constructor
因此沒有嘗試把 1, 3 用 emplace 改寫

沒有留言:

張貼留言