28

次の点を考慮してください。

std::string make_what_string( const std::string &id );

struct basic_foo
{
    basic_foo( std::string message, std::string id );
};

struct foo
    : public basic_foo
{
    foo::foo( std::string id)
        : basic_foo( make_what_string( id ), std::move( id ) ) // Is this valid?
    {
    }
};

C++ ではパラメータの評価順序が規定されていないため、

basic_foo( make_what_string( id ), std::move( id ) )

上記のコードは有効です。

私はそれstd::moveがキャストにすぎないことを知っていますが、 std::string move ctor はいつ実行されますか? すべての引数が評価された後、基本コンストラクターを呼び出す時が来ましたか? それとも、これはパラメータの評価中に行われますか? 言い換えると:

コンパイラはこれを行いますか:

std::string &&tmp2 = std::move(id);
std::string tmp1 = make_what_string(id);
basic_foo(tmp1, tmp2);

これは有効です。またはこれ:

std::string tmp2 = std::move(id);
std::string tmp1 = make_what_string(id);
basic_foo(tmp1, tmp2);

これは無効です。どちらの場合も、順序は「予期しない」ものであることに注意してください。

4

2 に答える 2

20

セクション 1.9 を参照してください。

特に断りのない限り、個々の演算子のオペランドおよび個々の式の部分式の評価は順不同です。

関数を呼び出すとき (関数がインラインであるかどうかに関係なく)、すべての値の計算と、任意の引数式、または呼び出された関数を指定する後置式に関連付けられた副作用は、本体のすべての式またはステートメントの実行前に順序付けられます。と呼ばれる機能。[: 異なる引数式に関連付けられた値の計算と副作用は順不同です。—<em>終わりのメモ]

問題は、パラメーターの初期化が引数式に関連する副作用と見なされるかどうかがあまり明確ではないことだと思います。ただし、セクション 5.2.2 によってバックアップされているようです。

各パラメーターの初期化と破棄は、呼び出し元の関数のコンテキスト内で発生します。

また、同じ段落には、もう少し明確にするメモもあります。

関数が呼び出されると、各パラメーター (8.3.5) は対応する引数で初期化されます (8.5、12.8、12.1)。[: このような初期化は、相互に不定に順序付けられます (1.9) —末尾の注記]

そうです、引数の初期化は互いに不定に順序付けられます。初期化は、次のいずれかの順序で発生する可能性があります。

std::string message = make_what_string(id);
std::string id = std::move( id );

std::string id = std::move( id );
std::string message = make_what_string(id);

2 番目のケースでmake_what_stringは、移動元の文字列を使用することになります。

したがって、std::move実際には何も移動しませんが、重要なことは、実際の移動も他の引数に関して順序付けされていないということです。

状態の移動コンストラクターの定義basic_string(basic_string&& str):

[...]strは値が指定されていない有効な状態のままです。

したがって、未定義の動作はありません。未指定の動作があります。

于 2013-03-28T11:43:15.457 に答える
7

それは本当に有効ではありません。関数の引数評価の順序は規定されていません。つまり、コンパイラがこのシーケンスを選択するかどうかはわかりません。

tmp1 = make_what_string(id);
tmp2 = std::move(id);
basic_foo(tmp1, tmp2);

またはこれ:

tmp1 = std::move(id);
tmp2 = make_what_string(id);  //id has already been moved from!
basic_foo(tmp2, tmp1);
于 2013-03-28T11:32:00.793 に答える