11

私は参照渡しを学んでいましたが、これが私が行ったテストです:

#include <iostream>

using namespace std;

int i = 0;

//If this is uncommented, compiler gives ambiguous definition error.
//void paramCheck (string s) {
//  cout << ++i << ". Param is var.\n";
//}

void paramCheck (const string& s) {
    cout << ++i << ". Param is const ref.\n";
}

void paramCheck (string& s) {
    cout << ++i  << ". Param is non-const ref.\n";
}

void paramCheck (const string&& s) {
    cout << ++i  << ". Param is const rvalue-reference.\n";
}

void paramCheck (string&& s) {
    cout << ++i  << ". Param is non-const rvalue-reference.\n";
}


int main(int argc, char **argv) {
    //Function call test
    paramCheck("");

    paramCheck(string{""});

    string s3{""};
    paramCheck(s3);

    const string s4{""};
    paramCheck(s4);

    //Illegal
    //string& s{""};
    //paramCheck(s);

    const string& s5{s3};
    paramCheck(s5);

    string&& s6{""};
    paramCheck(s6);

    //Illegal
    //const string&& s{s1};
    //onstFP(s);

    //Reference test
    string a = s3;
    a = "a changed s3";
    cout << s3;

    {
    string& b = s3;
    b = "b changed after assigning s3\n";
    cout << "s3 is now " <<s3;

    b = s4;
    b = "b changed after assigning s4\n";
    cout << "s3 is now " <<s3;
    cout << "s4 is now " <<s4;
    }

    cin.get();
    return 0;
}

そして、ここに私が得る結果があります:

1. Param is non-const rvalue-reference.
2. Param is non-const rvalue-reference.
3. Param is non-const ref.
4. Param is const ref.
5. Param is const ref.
6. Param is non-const ref.
s3 is now b changed after assigning s3
s3 is now b changed after assigning s4
s4 is now

私の質問は:

  1. 定数式を渡すと、常に非定数右辺値参照がトリガーされますか? どのような条件下で、定数の右辺値参照がトリガーされますか (そして、なぜ s6 はそれをトリガーしないのですか?)

  2. 非定数参照と定数右辺値参照が違法なのはなぜですか?

  3. a は s3 を変更できないと思っていましたが、なぜ内部スコープの b が s3 を変更できるのでしょうか? 新しいオブジェクト s3 を b に割り当てることが新しい参照を割り当てる場合、s4 をそれに割り当てると、s3 が変更され、その後 s4 が空になるのはなぜですか?

質問が多すぎてすみません... すべての質問に答えたらポイントを増やします:) 参照は、ポインタからまったく新しいレベルへの私の混乱をもたらします.


ポイントの増やし方がわからないので…バウンティ対象になるまで2日待ってから回答を選択します。

4

5 に答える 5

11

まずはコード

paramCheck(""); //constructs a temporary. temporaries bind to `string&&`
paramCheck(string{""}); //constructs a temporary. temporaries bind to `string&&`
string s3{""};
paramCheck(s3); //passes a reference to an existing string: `string&`
const string s4{""};
paramCheck(s4); //passes a reference to an existing string+const: `const string&`
//Illegal
//string& s{""}; //cannot assign a temporary to a non-const l-reference
                 //what would s refer to when the temporary "dies"?
                 //`const string&` would have worked though
//paramCheck(s); //passes a reference to an existing string+const: `const string&`
const string& s5{s3}; //s5 is s3, but with `const`. 
paramCheck(s5); //passes a reference to an existing string+const: `const string&`
string&& s6{""}; //r-references extend the life of temporaries.
paramCheck(s6); //passes a reference to an existing strong: `string&`
//const string&& s{s1}; //temporaries can be extended by `T&&` or `const T&` only.

//Reference test
string a = s3; //a is a _copy_ of s3
a = "a changed s3"; //so changing the copy doesn't effect the origional.
cout << s3; //s3 is still blank, it hasn't changed.

{
string& b = s3; //b isn't really a "reference" to `s3`".  `b` _IS_ `s3`.
b = "b changed after assigning s3\n"; //since `b` IS `s3`, this changes `s3`.
cout << "s3 is now " <<s3;

b = s4; //`b` _IS_ `s3`, so you just changed `s3` again.
b = "b changed after assigning s4\n";
cout << "s3 is now " <<s3;
cout << "s4 is now " <<s4; //s4 is still blank, it hasn't changed.
}

それから質問:

定数式を渡すと、常に非定数右辺値参照がトリガーされますか? どのような条件下で、定数の右辺値参照がトリガーされますか (そして、なぜ s6 はそれをトリガーしないのですか?)

既存のオブジェクトは、それらが const かどうstring&const string&に応じて渡されます。としてコピーすることもできますstring。一時ファイルは as として渡されstring&&ますが、 as にコピーすることもできますstring。をトリガーする方法ありますconst string&&が、そうする理由はないので、問題ではありません。それらはここに示されています

非定数参照と定数右辺値参照が違法なのはなぜですか?

const string&標準では、およびのみが一時的な寿命を延ばすと具体的に述べていますが、なぜおよびstring&&についても言及しなかったのかはわかりません。string&const string&&

a は s3 を変更できないと思っていましたが、なぜ内部スコープの b が s3 を変更できるのでしょうか? 新しいオブジェクト s3 を b に割り当てることが新しい参照を割り当てる場合、s4 をそれに割り当てると、s3 が変更され、その後 s4 が空になるのはなぜですか?

bへの参照として初期化しましたs3。コピペじゃなくて参考までに。つまり、現在は何があっても永遠bを指します。と入力すると、それは とまったく同じです。と入力すると、それは とまったく同じです。参考書とはそういうものです。それらを「再装着」することはできません。s3 b = "b changed after assigning s3\n";s3 = "b changed after assigning s3\n";b = s4;s3 = s4

于 2013-07-31T21:02:16.140 に答える
6

右辺値は右辺値参照と const 左辺値参照にバインドできます。

void foo(const string&);
void bar(string&&);

foo(string{});
bar(string{});

ただし、右辺値は非 const 左辺値参照にバインドできません。オーバーロードの解決では、一時変数を const 左辺値参照にバインドするよりも、右辺値参照にバインドすることを優先します。

void foo(const string&);
void foo(string&&);

foo(string{});           // will call the second overload

左辺値は、左辺値参照にのみバインドできます。ただし、constこれを制限することに注意してください。

const string do_not_modify_me;
string& modify_me = do_not_modify_me;  // not allowed, because `do_not_modify_me`
modify_me += "modified";               // shall not be modified: declared as `const`

std::move左辺値を右辺値参照にバインドすることもできます。

string s;
string&& r = std::move(s);

これは、右辺値の概念が、その内容をリサイクルできるということであるためです。たとえば、動的に割り当てられたメモリの所有権を主張します。操作後もオブジェクトにアクセスできる場合、これは危険な場合があるため、左辺値には明示的なstd::moveものが必要です。


paramCheck("");         // a string literal is an lvalue (!)
                        // see [expr.prim.general]/1
                        // but it is implicitly converted to a `std::string`,
                        // creating a `string` temporary, a rvalue

paramCheck(string{""}); // a temporary is an rvalue

string s3{""};
paramCheck(s3);         // the variable `s3` is an lvalue of type `string`

const string s4{""};
paramCheck(s4);         // the variable `s4` is an lvalue of type `const string`

//Illegal
//string& s{""};        // can't bind a temporary to a non-const lvalue ref
//paramCheck(s);

const string& s5{s3};
paramCheck(s5);         // the variable `s5` is a lvalue of type `const string`

string&& s6{""};        // binding a temporary to a rvalue-ref (allowed)
paramCheck(s6);         // the variable `s6` is an lvalue (!) - it has a name

//Illegal
//const string&& s{s1}; // `s1` has not been declared
//onstFP(s);

//Reference test
string a = s3;          // copy the contents of `s3` to a new string `a`
a = "a changed s3";     // overwrite contents of `a`
cout << s3;

{
string& b = s3;         // `b` refers to `s3` now (like an alias)
b = "b changed after assigning s3\n";
cout << "s3 is now " <<s3;

b = s4;                 // copy the contents of `s4` to `b` (i.e. to `s3`)
b = "b changed after assigning s4\n";
cout << "s3 is now " <<s3;
cout << "s4 is now " <<s4;
}

定数式を渡すと、常に非定数右辺値参照がトリガーされますか? どのような条件下で、定数の右辺値参照がトリガーされますか (そして、なぜ s6 はそれをトリガーしないのですか?)

constexpr定数式には、右辺値であるor const、または一時変数のいずれかで宣言されたオブジェクト (の左辺値から右辺値への変換) のみを含めることができます。したがって、知る限り、定数式は非定数左辺値を生成できません。


非定数参照と定数右辺値参照が違法なのはなぜですか?

実際にはどちらも許可されています。右辺値参照は私には意味がありませconstんが、const左辺値参照も使用できます。


a は s3 を変更できないと思っていましたが、なぜ内部スコープの b が s3 を変更できるのでしょうか? 新しいオブジェクト s3 を b に割り当てることが新しい参照を割り当てる場合、s4 をそれに割り当てると、s3 が変更され、その後 s4 が空になるのはなぜですか?

参照の初期化と、参照として宣言した名前への割り当ての違いについて混乱していると思います。

于 2013-07-31T20:58:21.810 に答える
3

定数式を渡すと、常に非定数右辺値参照がトリガーされますか? どのような条件下で、定数の右辺値参照がトリガーされますか (そして、なぜ s6 はそれをトリガーしないのですか?)

定数式の場合は?なし。何かがバインドさconst&&れるのは、それが既にconst. その場合でも、変数の場合は明示的なキャストが必要になります (以下を参照)。

非定数参照と定数右辺値参照が違法なのはなぜですか?

私はあなたがこれらについて話していると仮定します:

//string& s{""};
//paramCheck(s);

//const string&& s{s1};
//onstFP(s);

は変数""ではないため、最初は不正です。std::stringしたがって、std::string一時的な from を構築する必要があり""ます。既存の文字列変数sへの非 const 参照です。テンポラリは変数ではないため、テンポラリへの非 const 参照を取ることはできません。

(存在しないという事実を無視して) C++ では、明示的な変換s1なしに変数への右辺値参照を取得できないため、2 番目は違法です。これが目的です。うまく動作します。std::moveconst string &&s{std::move(s3)}

a は s3 を変更できないと思っていましたが、なぜ内部スコープの b が s3 を変更できるのでしょうか? 新しいオブジェクト s3 を b に割り当てることが新しい参照を割り当てる場合、s4 をそれに割り当てると、s3 が変更され、その後 s4 が空になるのはなぜですか?

まず、問題なく変更できますs3bへの参照ですs3。これらは同じオブジェクトの 2 つの名前です。b残りに関しては、作成後に参照されるオブジェクトを変更することはできませんbbは参照を開始するs3ため、常に参照します。したがって、によって参照される任意のオブジェクトb = s4にコピーすることを意味します。s4bs3

s4常に空だったので、後で空になります。の文字列を割り当てました。だから空です。

于 2013-07-31T21:02:04.987 に答える
2

Foo&&右辺値参照と考えるのはやめるべきです。物事が何に結びついているかを考えてください。

受け取る関数は、一時的なs、または一時的としてマークされたsFoo&&にのみバインドされます。FooFoo

この一時的なマーキングは持続しません。variableFoo&& fooがあり、それを使用する場合、使用時に一時的としてマークされません。何かを一時的であるとマークすることは、すぐにのみ行うことができます。 を返す関数によってFoo&&、または匿名Fooを返すことによって、すぐに使用すると一時的と見なされます。

データを一時的なものとしてマークする標準的な方法は、(A) 一時的な の匿名インスタンスでFooある、(B)std::moveのインスタンスで呼び出したFoo、(C)std::forward<Foo>のインスタンスで呼び出した、ですFoo

実際に&&は、ユニバーサル参照と呼ばれるものと、一時的にバインドする参照の両方で使用されます。型推論のコンテキストでは、左辺値参照は、右辺値参照よりも「優先」されるようにT&&することで、a に格納できます。これは、条件付きで移動するために呼び出す必要がある状況です。TFoo&std::forward

要するに、使用する一般的な有効なスポットが 4 つあります&&

  • 関数またはメソッドの引数リストで移動元にしたい引数を取るとき。
  • template関数の引数 で完全転送とユニバーサル参照手法を使用している場合。
  • 完全に転送されたパラメーターを戻り値にパススルーする場合。
  • ユニバーサル参照手法を実行して、関数スコープで一時的なものへの参照を作成する場合 (たとえば、for(auto&& i:x))。

名前付き変数を使用すると、変数または変数&&とほぼ同じように機能します。一時的なものとして扱われる方法で使用するには、 を使用するか、ユニバーサル参照コンテキストでは、条件付きで を使用する必要があります。&const &std::movestd::forwardstd::move

于 2013-07-31T21:05:56.117 に答える