c++ - operator - std move destructor




C++ 0x rvalue参照と一時的な (4)

GCCはFCDに従ってそれを間違っている。 FCDは、 8.5.3で参照結合について述べている

  • 参照が左辺値参照であり、初期化式が[左辺値/クラス型]の場合...
  • それ以外の場合、参照は不揮発性のconst型への左辺の参照でなければならない(すなわち、cv1はconstでなければならない)か、参照は右辺の参照とし、初期化子の式は右辺値または関数型でなければならない。

初期化子が左辺値なので、 std::string &&への呼び出しのケースはどれも一致しません。 そのトップレベルの弾丸は既に値を必要とするため、一時的な値を作成する場所には達しません。

現在、オーバーロードの解決では、暗黙的な変換シーケンスが存在するかどうかを参照バインディングを直接使用することはありません。 代わりに、それは13.3.3.1.4/2で言う

参照型のパラメータが引数式に直接バインドされていない場合、変換式は、引数式を13.3.3.1に従って参照の基になる型に変換するために必要なものです。

したがって、実際にはその勝者がその議論に結びつくことができなくても、過負荷解決によって勝者が決まる。 例えば:

struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };

void f(int&);
void f(B);
int main() { A a; f(a.bits); }

8.5参照結合は、ビット値フィールドを左辺値参照にバインドすることを禁じます。 しかし、オーバーロード分解能は、変換シーケンスがintに変換するシーケンスであることを示しているため、コールが後で行われるときにコールが不正な形式になっても成功します。 したがって、私のビットフィールドの例は不正です。 Bバージョンを選択した場合は成功したが、ユーザー定義の変換が必要だった。

ただし 、そのルールには2つの例外があります。 これらは

13.3.1を参照する暗黙のオブジェクトパラメータを除いて、左辺値への左辺値の参照を右辺値にバインドするか、左辺値に左辺値にバインドする必要がある場合、標準の変換シーケンスを形成することはできません。

したがって、次の呼び出しが有効です。

struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };

void f(int&); /* binding an lvalue ref to non-const to rvalue! */
void f(B);
int main() { A a; f(1); }

したがって、あなたの例はconst T&バージョンを呼び出します

void f(const std::string &);
void f(std::string &&); // would bind to lvalue!

void g(const char * arg) { f(arg); }

しかし、 f(arg + 0)と言うと、rvalueを作成するので、2番目の関数は実行可能です。

(私はcomp.std.c ++でこの質問のバリエーションを尋ねましたが、回答は得られませんでした)。

このコードでf(arg)呼び出すと、なぜf(arg)のconst refのオーバーロードが呼び出されるのですか?

void f(const std::string &); //less efficient
void f(std::string &&); //more efficient

void g(const char * arg)
{
     f(arg);
}

私の直感では、 argが何であっても一時的に変換される必要があり、一時的な値は左辺値よりも右辺値の方が良いため、 f(string &&)オーバーロードを選択する必要があります。

これはGCCと MSVCで起こることではありません(編集:Thanks Sumant:GCC 4.3-4.5では発生しません)。 少なくともG ++と MSVCでは、中間値が一時的に作成されていても 、左辺値はrvalue参照引数にバインドされません。 実際、const refのオーバーロードが存在しない場合、コンパイラはエラーを診断します。 しかし、 f(arg + 0)またはf(std::string(arg))書くと、期待通りのrvalue参照のオーバーロード選択されます。

私のC ++ 0x標準の読み方からは、const lvalue ref引数を渡すときと同じように、 f(string &&)が実行可能かどうかを検討するときには、const char *の文字列への暗黙的変換を考慮する必要があります。 セクション13.3(過負荷解決)は、あまりにも多くの場所でr値の参照とconst参照を区別しません。 また、左辺値が右辺値(13.3.3.1.4 / 3)にバインドされないようにするルールは、中間の一時的なものがある場合は適用しないでください。結局のところ、一時的なものから完全に安全に移動することができます。

これは:

  1. 私は実装された振る舞いが意図された振る舞いであるところで、標準を誤読/誤解しています。私の例が振る舞いにふさわしい理由がありますか?
  2. コンパイラのベンダーがどうにかして間違っていたのですか? または、共通の実装戦略に基づく間違い? または、他のベンダーによってコピーされたGCC(このlvalue / rvalue参照バインディングルールが最初に実装された)などの間違い?
  3. 標準の欠陥、意図しない結果、または明らかにすべき何か?

編集:関連するフォローオンの質問があります。C ++ 0x rvalue references - lvalues-rvalue binding


あなたが私に尋ねるならば、標準の現在のドラフトの多くのことが明確化を必要とします。 そしてコンパイラはまだ開発中ですので、彼らの助けを信じるのは難しいです。

あなたの直感が正しいことははっきりしているように見えます...あらゆる種類の一時的なものは、正の値の参照にバインドされています。 例えば、新しい「タクソノミ」セクションである§3.10は、一時的なものをrvaluesと断定的に定義しています。

問題は、RR引数指定が一時的な作成を呼び出すのに不十分であることがあります。 §5.2.2/ 5: "パラメータがconst参照型の場合、必要に応じて一時オブジェクトが導入されます。" それは疑わしい排他的に聞こえる。

§13.3.3.1/ 6で亀裂から再び滑り落ちているように見えます(強調する鉱山)

パラメータ型が参照でない場合、暗黙の変換シーケンスは引数式からパラメータのコピー初期化をモデル化します。 暗黙の変換シーケンスは、引数式をパラメータのタイプのprvalueに変換するために必要な変換シーケンスです。

コピー初期化string &&rr = "hello"; GCCで正常に動作します。

編集:実際には、問題はGCCの私のバージョンに存在しません。 私はまだ、ユーザー定義の変換シーケンスの第2の標準変換が、r値参照の作成にどのように関係しているかを把握しようとしています。 (RR形成はまったく変換ですか、それとも5.2.2 / 5のようなばかばかしいごちそうによって決まりますか?)



私はg ++でDougが言及した動作を見ませんでした。 g ++ 4.5と4.4.3はどちらもf(string &&)を期待通りに呼び出しますが、VS2010はf(const string &)呼び出します。 どのg ++​​バージョンを使用していますか?





rvalue-reference