C++11 のムーブセマンティクスがよく分からないので実験してみる。
class Bean { public: Bean() :color(0) ,shape(0) { std::cout << "constructor\n"; } Bean(const Bean& other) : color(other.color) , shape(other.shape) { std::cout << "Bean copy constructer\n"; } Bean(Bean&& other) noexcept : color(std::exchange(other.color, 0)) , shape(std::exchange(other.shape, 0)) { std::cout << "Bean move constructer\n"; } int color; int shape; }; class Bag { public: Bag() { } std::vector<Bean> beans; }; int main(int argc, const char * argv[]) { printf("1--\n"); { auto bag = Bag(); Bean bean; // constructor bag.beans.push_back(bean); // copy } printf("2--\n"); { auto bag = Bag(); Bean bean; // constructer bag.beans.emplace_back(bean); // copy } printf("3--\n"); { auto bag = Bag(); bag.beans.emplace_back(); // move } printf("4--\n"); { auto bag = Bag(); Bean bean; // constructor bag.beans.emplace_back(std::move(bean)); // move } printf("5--\n"); { auto bag = Bag(); Bean bean; // constructor bag.beans.push_back(std::move(bean)); // move } printf("6--\n"); { auto bag = Bag(); bag.beans.push_back(Bean()); // constructor + move } printf("7--\n"); { auto bag = Bag(); bag.beans.emplace_back(Bean()); // constructor + move } // 1, 2がcopyが走っていて最悪なケース // 4, 5, 6, 7 は生成処理が1回、moveが1回 // 3 は生成処理が1回で最も効率的なケース
この例では要素の型が説明のためintになっているが、一般に巨大なクラスだと考えておく。
1-- constructor copy 2-- constructor copy 3-- constructor 4-- constructor move 5-- constructor move 6-- constructor move 7-- constructor move
次にcopy operatorとmove operatorの動作をみてみよう。
Bean& operator=(const Bean& other) { std::cout << "Bean copy operator\n"; if (this != &other) { color = other.color; shape = other.shape; } return *this; } Bean& operator=(Bean&& other) noexcept { std::cout << "Bean move operator\n"; if (this != &other) { color = other.color; shape = other.shape; } return *this; } { Bean bean1; Bean bean2; bean2 = bean1; } printf("--\n"); { Bean bean1; Bean bean2; bean2 = std::move(bean1); }
Bean copy operator -- Bean move operator
こちらは意図通り、左辺値を渡した時はcopy、右辺値を渡した時はmoveとなった。