yohhoyの日記

技術的メモをしていきたい日記

危険な自己ムーブ代入:x = std::move(x)

C++標準ライブラリ提供クラスのムーブ代入演算子による自己代入操作について。

2019-09-02追記:C++17現在はLWG 2468によって、自己ムーブ代入操作による未定義動作(undefined behavior)は回避される。ただし別途規定のない場合、自己ムーブ代入後は “有効だが未規定な状態(valid but unspecified state)” となることに注意(→id:yohhoy:20120616)。

2021-09-11追記:C++標準ライブラリ全域にわたる自己ムーブ代入への対処の明確化のため、LWG 2839が採択されC++2b(C++23) WD仕様に適用されている。ユーザからみた振る舞いはC++17時点と同じ。またClang 3.6*1から自明な自己ムーブ代入を検知する-Wself-move警告オプションが追加された。

2022-09-29追記:GCC 13から-Wself-move警告オプションが追加される。

要約

  • C++標準ライブラリ提供のクラス(クラステンプレート)では、自己ムーブ代入操作は未定義動作(undefined behavior)を引き起こす。
  • 例外的に、文字列クラステンプレートstd::basic_stringは自己ムーブ代入操作がwell-definedとなっている(何もしない)。
  • おまけ:ユーザ定義型における自己ムーブ代入操作は無関係。単にユーザ定義型でのムーブ代入演算子実装に依存する。
std::vector<int> v = { /*...*/ };
std::string s = /*...*/;

v = std::move(v);  // NG: 未定義動作
s = std::move(s);  // OK: 何もしない

関数(含むメンバ関数)の引数にrvalue参照型(T&&)を取る場合、個別の要件が無いかぎりは、同引数がユニークなオブジェクトを指す、つまりメンバ関数であれば引数が*thisを指すことが無いと仮定した標準ライブラリ実装が許される。C++11(N3337) 17.6.4.9/p1, 21.4.2/p23*2より一部引用(下線部は強調)。

1 Each of the following applies to all arguments to functions defined in the C++ standard library, unless explicitly stated otherwise.

  • (snip)
  • If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument. [Note: If the parameter is a generic parameter of the form T&& and an lvalue of type A is bound, the argument binds to an lvalue reference (14.8.2.1) and thus is not covered by the previous sentence. -- end note] [Note: If a program casts an lvalue to an xvalue while passing that lvalue to a library function (e.g. by calling the function with the argument move(x)), the program is effectively asking that function to treat that lvalue as a temporary. The implementation is free to optimize away aliasing checks which might be needed if the argument was an lvalue. -- end note]

basic_string<E,T,A>& operator=(basic_string<E,T,A>&& str) noexcept;
23 If *this and str are the same object, the member has no effect.

関連URL

*1:http://blog.llvm.org/2015/02/llvm-36-release.html

*2:basic_string クラステンプレートのテンプレートパラメータ名は略記。