23

std::move2013 年の GoingNative の講演で、Scott Meyersは、生成されたコードが実際に移動を実行するという保証はないことを指摘しました。

例:

void foo(std::string x, const std::string y) {
  std::string x2 = std::move(x); // OK, will be moved
  std::string y2 = std::move(y); // compiles, but will be copied
}

ここでは、ムーブ コンストラクターを適用できませんが、オーバーロードの解決のために、代わりに通常のコピー コンストラクターが使用されます。このフォールバック オプションは、C++98 コードとの下位互換性にとって重要な場合がありますが、上記の例では、プログラマが意図したものではない可能性が高いです。

移動コンストラクターが呼び出されるようにする方法はありますか?

たとえば、巨大な行列を移動するとします。アプリケーションが移動するマトリックスに本当に依存している場合、移動が不可能な場合にすぐにコンパイル エラーが発生するのは素晴らしいことです。(そうしないと、パフォーマンスの問題が単体テストを簡単にすり抜ける可能性があり、プロファイリングを行った後にのみ発見されます。)

これを保証された動きと呼びましょうstrict_move。次のようなコードを記述できるようにしたいと考えています。

void bar(Matrix x, const Matrix y) {
  Matrix x2 = strict_move(x); // OK
  Matrix y2 = strict_move(y); // compile error
}

出来ますか?

編集:

素晴らしい答えをありがとう!私の質問を明確にするための正当な要求がいくつかありました。

  • strict_move入力が const の場合は失敗しますか?
  • strict_move結果が実際の移動操作につながらない場合 (コピーが移動と同じくらい高速である場合でもconst complex<double>)、失敗する必要がありますか?
  • 両方?

私の最初のアイデアは非常に漠然としていました。Scott Meyers の例は非常に憂慮すべきものだと考えたので、コンパイラでそのような意図しないコピーを防ぐことができるかどうか疑問に思いました。

Scott Meyers は講演の中で、一般的なコンパイラの警告はオプションではないと述べました。代わりに、「これは常に移動操作になる必要があり、コピーはこの特定の型には高すぎると 100% 確信しています」のようなものをコンパイラに伝えたいと考えています。

strict_moveしたがって、どちらの場合も失敗するはずだと率直に言ったでしょう。その間、私は何が最善かわかりません。私が考慮しなかった別の側面はnoexcept.

私の側では、 の正確なセマンティクスstrict_moveはオープンです。重大な欠点を持たずに、コンパイル時の愚かな間違いを防ぐのに役立つものはすべて問題ありません。

4

3 に答える 3

19

strict_moveを検出している将軍を書くことはお勧めしませんconst。それは本当にあなたが探しているものではないと思います。これにフラグを立てたいですかconst complex<double>、それとも constpair<int, int>ですか? これらのタイプは、移動するのと同じ速さでコピーします。それらにフラグを立てることは、刺激になるだけです。

これを行いたい場合は、代わりにタイプが であるかどうかを確認することをお勧めしますnoexcept MoveConstructible。これは完全に機能しstd::stringます。のコピー コンストラクターstringが誤って呼び出された場合、それは noexcept ではないため、フラグが立てられます。しかし、 のコピー コンストラクターpair<int, int>が誤って呼び出された場合、本当に気にしますか?

これがどのように見えるかのスケッチは次のとおりです。

#include <utility>
#include <type_traits>

template <class T>
typename std::remove_reference<T>::type&&
noexcept_move(T&& t)
{
    typedef typename std::remove_reference<T>::type Tr;
    static_assert(std::is_nothrow_move_constructible<Tr>::value,
                  "noexcept_move requires T to be noexcept move constructible");
    static_assert(std::is_nothrow_move_assignable<Tr>::value,
                  "noexcept_move requires T to be noexcept move assignable");
    return std::move(t);
}

is_nothrow_move_assignableクライアントが lhs を構築しているのか割り当てているのかがわからないため、同様にチェックすることにしました。

過負荷になるとは思わないためstatic_assert、外部ではなく内部を選択しました。トリガーされたときに、より明確なエラーメッセージが表示されます。enable_ifnoexcept_movestatic_assert

于 2013-09-05T23:19:48.700 に答える