Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass by reference, constant reference, rvalue-reference, or constant rvalue-reference?

I was learning passing by reference, and here is the test I did:

#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;
}

And here is the result I get:

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

My question is:

  1. If we pass a constant expression, it always triggers non-constant rvalue-reference? Under what condition it will trigger constant rvalue-reference (and why s6 is not trigging it?)

  2. Why non-constant reference and constant rvalue-reference are illegal?

  3. I expected a cannot change s3, but why b in the inner scope can change s3? If assigning a new object s3 to b is assigning a new reference, why when I assign s4 to it and s3 got changed and s4 is empty afterwards?

Sorry for asking too many questions... I will increase the points when all questions are answered :) The reference just brings my confusion from pointer to a whole new level.


I don't know how to increase the point... so will wait for 2 days till eligible for bounty then choose the answer.

like image 499
SwiftMango Avatar asked Jul 31 '13 20:07

SwiftMango


People also ask

Can you pass an rvalue by reference?

You can pass an object to a function that takes an rvalue reference unless the object is marked as const . The following example shows the function f , which is overloaded to take an lvalue reference and an rvalue reference. The main function calls f with both lvalues and an rvalue.

How do you pass a constant reference?

When you pass by const reference, you take the argument in by reference (avoiding making any copies of it), but cannot make any changes to the original object (much as would happen when you would take the parameters in by value).

What is the difference between L value and R value references?

“l-value” refers to a memory location that identifies an object. “r-value” refers to the data value that is stored at some address in memory. References in C++ are nothing but the alternative to the already existing variable. They are declared using the '&' before the name of the variable.

What are rvalue references good for?

Rvalue references is a small technical extension to the C++ language. Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higer performance and more robust libraries.


1 Answers

First the code

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.
}

Then the questions:

If we pass a constant expression, it always triggers non-constant rvalue-reference? Under what condition it will trigger constant rvalue-reference (and why s6 is not trigging it?)

Existing objects will pass as string& or const string& depending on if they're const or not. They can also be copied in as string. Temporaries will pass as string&&, but can also be copied in as string. There are ways to trigger const string&&, but there's no reason to do so ever, so it doesn't matter. They're shown here.

Why non-constant reference and constant rvalue-reference are illegal?

The standard specifically says that only const string& and string&& will extend the lives of temporaries, though I'm not certain why they didn't also mention string& and const string&&.

I expected a cannot change s3, but why b in the inner scope can change s3? If assigning a new object s3 to b is assigning a new reference, why when I assign s4 to it and s3 got changed and s4 is empty afterwards?

You initialized b as a reference to s3. Not a copy, but a reference. That means b now refers to s3 forever, no matter what. when you typed b = "b changed after assigning s3\n";, that's exactly the same as s3 = "b changed after assigning s3\n";. When you typed b = s4;, that's exactly the same as s3 = s4. That's what a reference is. They cannot be "reseated".

like image 103
Mooing Duck Avatar answered Sep 16 '22 11:09

Mooing Duck