通过引用,常量引用,rvalue-reference或常量rvalue-reference传递?

tex*_*uce 11 c++ parameter-passing rvalue-reference c++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;
}
Run Code Online (Sandbox Code Playgroud)

这是我得到的结果:

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
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  1. 如果我们传递一个常量表达式,它总是触发非常量rvalue-reference?在什么条件下它会触发恒定的右值参考(以及为什么s6没有触发它?)

  2. 为什么非常量引用和常量右值引用是非法的?

  3. 我期望一个不能改变s3,但为什么b在内部范围内可以改变s3?如果为b分配一个新对象s3正在分配一个新的引用,为什么当我为它分配s4并且s3被更改并且之后s4为空?

很抱歉问了太多问题......当所有问题都得到解答时我会增加积分:)这个参考只会让我从指针到一个全新的水平混乱.


我不知道如何增加点...所以将等待2天,直到有资格获得赏金,然后选择答案.

Moo*_*uck 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.
}
Run Code Online (Sandbox Code Playgroud)

那么问题:

如果我们传递一个常量表达式,它总是触发非常量rvalue-reference?在什么条件下它会触发恒定的右值参考(以及为什么s6没有触发它?)

现有对象将通过string&const string&取决于它们是否为常量.它们也可以复制为string.Temporaries将传递为string&&,但也可以复制为string.有一些方法可以触发const string&&,但没有理由这样做,所以没关系.他们在这里展示.

为什么非常量引用和常量右值引用是非法的?

该标准明确指出,只有const string&string&&将延长临时工的生活,虽然我不能肯定他们为什么不也不在话下string&const string&&.

我期望一个不能改变s3,但为什么b在内部范围内可以改变s3?如果为b分配一个新对象s3正在分配一个新的引用,为什么当我为它分配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.这就是参考资料.他们不能"重新安置".

  • 根据SOF的规定,我不认为这会被视为建设性的评论,但我需要说,我喜欢通过你的答案来学习和重新学习.@ Maioo Duck.你完全以一种对我有意义的方式解释事物.我知道我不是唯一一个有这种感觉的人,如果我的工作没有阻止SOF的聊天功能,我会在更合适的环境中做到这一点.但是,谢谢你给我的帮助,无论是直接与否.我的个人资料中的电子邮件,如果您愿意的话,我想和您聊聊编码.:) (4认同)
  • @Mehrdad:编辑了问题中立即想到的两种方式,尽管我确定还有其他方法.在const变量上调用`std :: move`可能适用于例如.这三种方式都很明确,你可能不会意外地找到它们 (2认同)

dyp*_*dyp 6

rvalues可以绑定到rvalue引用和const lvalue引用,例如

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

foo(string{});
bar(string{});
Run Code Online (Sandbox Code Playgroud)

但是rvalue不能绑定到非const左值引用.重载分辨率更喜欢绑定临时值到rvalue-refs,而不是将它们绑定到const lvalue refs:

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

foo(string{});           // will call the second overload
Run Code Online (Sandbox Code Playgroud)

左值只能绑定到左值引用.但请注意,这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`
Run Code Online (Sandbox Code Playgroud)

你也可以左右std::move将它们绑定到右值引用:

string s;
string&& r = std::move(s);
Run Code Online (Sandbox Code Playgroud)

这是因为rvalue 的概念是您可以回收其内容,例如声明它已动态分配的内存的所有权.如果在操作后仍然可以访问对象,则这可能很危险,因此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;
}
Run Code Online (Sandbox Code Playgroud)

如果我们传递一个常量表达式,它总是触发非常量rvalue-reference?在什么条件下它会触发恒定的右值参考(以及为什么s6没有触发它?)

常量表达式只能包含(左值constexpr或右值转换)声明的对象const或者临时值,它们是rvalues.因此,AFAIK,常量表达式不能产生非常数左值.


为什么非常量引用和常量右值引用是非法的?

实际上,两者都是允许的.虽然constrvalue refs对我没有任何意义,但你也可以使用constlvalue-refs.


我期望一个不能改变s3,但为什么b在内部范围内可以改变s3?如果为b分配一个新对象s3正在分配一个新的引用,为什么当我为它分配s4并且s3被更改并且之后s4为空?

我认为你对引用的初始化和分配给你声明为引用的名称之间的区别感到困惑.