17

この質問は、C++11 標準ライブラリのいくつかの関数の仕様に関するものです。これらの関数は、引数を右辺値参照として受け取りますが、すべての場合にそれらを消費するわけではありません。一例は std::unordered_set<T>::insert(T&&)です。

Tコンテナ内に要素がまだ存在しない場合、このメソッドが移動コンストラクタを使用してコンテナ内に要素を構築することは明らかです。しかし、要素がコンテナーに既に存在する場合はどうなるでしょうか。この場合、オブジェクトを変更する理由はないと確信しています。ただし、私の主張を裏付ける C++11 標準には何も見つかりませんでした。

これが興味深い理由を示す例を次に示します。次のコードは std::cin から行を読み取り、最初に出現した重複行を削除します。

std::unordered_set<std::string> seen;
std::string line;
while (getline(std::cin, line)) {
    bool inserted = seen.insert(std::move(line)).second;
    if (!inserted) {
        /* Is it safe to use line here, i.e. can I assume that the
         * insert operation hasn't changed the string object, because 
         * the string already exists, so there is no need to consume it. */
        std::cout << line << '\n';
    }
}

どうやら、この例は GCC 4.7 で動作します。しかし、それが標準に従って正しいかどうかはわかりません。

4

3 に答える 3

6

標準(17.4.6.9)でこのメモを見つけました:

[: プログラムがその左辺値をライブラリ関数に渡すときに (たとえば、引数を指定して関数を呼び出すことによって) 左辺値を xvalue にキャストする場合、move(x)プログラムは実質的にその左辺値を一時的なものとして扱うよう関数に要求しています。実装は、引数が左辺値である場合に必要になる可能性のあるエイリアシング チェックを自由に最適化して排除します。— エンドノート]

それはあなたの質問に直接答えるものではありませんが、ライブラリ関数への引数を一時的なものとして効果的に「与えた」ことを示しているので、呼び出した後はその値に依存しませんinsert。私が知る限り、ライブラリの実装は、後でコンテナーに値を保持しないと判断した場合でも、パラメーターから移動する資格があります。

于 2012-04-06T13:05:11.453 に答える
1

insert§23.2.5/Table 103 の順序付けられていない連想コンテナーで指定されているセマンティクスでinsertは、挿入が失敗した場合に引数のムーブ コンストラクターが呼び出されるかどうかが指定されていません。

a_uniq.insert(t)

戻り値:pair<iterator, bool>

必須:t非 const 右辺値式の場合、TX に MoveInsertable にする必要があります。それ以外の場合は、 T CopyInsertable into になりXます。

効果:tのキーと同等のキーを持つ要素がコンテナにない場合にのみ 挿入しますt。返されたペアの bool コンポーネントは挿入が行われるかどうかを示し、反復子コンポーネントは のキーと同等のキーを持つ要素を指しますt

ただし、 の仕様は (表 103 からも) より明確であり、必要な保証を得るemplace代わりにそれを使用できるはずです。insert

a_uniq.emplace(args)

戻り値:pair<iterator, bool>

Requires: T は、EmplaceConstructible into Xfrom args でなければなりません。

効果: std::forward(args)... で構築されたTオブジェクトを挿入するのは、コンテナ内に のキーと同等のキーを持つ要素がない場合のみです。返されるペアの bool コンポーネントは、挿入が行われ、ペアの反復子コンポーネントが のキーと同等のキーを持つ要素を指している場合にのみ true になります。ttt

T私はこれを ((構築されたオブジェクトを挿入しますt...) (コンテナに要素がない場合のみ ...))を意味すると解釈します。つまり、挿入と構築の両方は、一致する要素が既に存在しない場合にのみ発生するコンテナ。オブジェクトが構築されていない場合、std::string渡された は決してムーブ コンストラクターに渡されないため、emplace呼び出しが失敗した後も有効です。

gcc 4.7.0 は をサポートしていないようですunordered_set::emplaceが、標準に含まれています (§23.5.6.1)

@NicolBolas がコメントで指摘したように、上記にもかかわらず、競合するエントリが既に存在する場合に aemplaceを構築しない関数を実装することは不可能です。T

したがって、標準に準拠した方法で必要なセマンティクスを取得する唯一の方法はfind、条件付きでinsertorを実行することemplaceです。

于 2012-04-06T16:25:42.757 に答える
0

正しいです。

もちろん、哲学を扱うときは、何でも疑問を呈することができますが、コンパイラは何とかしなければなりません。

デザイナーの選択は、移動を実行するには、移動する場所がなければならないというものでした。そのような場所がなければ、移動は起こりません。

&& を取る関数が何であれ、パラメータが「一時的」であると仮定することに注意してください。データを「盗まない」場合、一時的は式の最後で破棄されます。一時性が(std::move を介して)強制された場合、オブジェクトは、独自のスコープによって破棄されるまで、いずれにしてもそこにとどまります。元のデータの有無にかかわらず。

于 2012-04-06T13:02:54.327 に答える