c++11 - c++函数返回对象的引用问题?
迷茫
迷茫 2017-04-17 13:28:58
0
4
408
string& Func(string& foo)
{
    return foo;
}

这个函数返回一个string的引用,我觉得调用它的时候会产生一个临时的引用变量,然后这个临时的引用变量绑定到foo上,对吗? 还是不会产生这个临时引用变量,直接返回foo?

比如下面这句赋值语句:

string foo;
string s = Func(foo);

等价于下面这两句:

string& temp = foo; //一个临时的引用变量绑定到foo上
string s = temp;

我理解的对吗? 返回引用其实还是会产生一个临时的引用变量吧(一个指针的大小),只不过用引用避免了对象的拷贝对吧? 不对的话求大大指出哪里理解的不对。。。

但如果返回一个临时的引用变量的话,又可以对它取地址这又怎么回事? 比如:

cout << &Func(foo);

这个临时变量应该是个右值才对? 难道对这个临时引用变量的所有操作都是对它引用的对象的操作?
或者说压根就不会生成这个临时引用变量,所有的操作都是在函数返回的对象上的操作? - - 晕了,真心求指出我的错误!!!

迷茫
迷茫

业精于勤,荒于嬉;行成于思,毁于随。

reply all(4)
迷茫
string& Func(string& foo)
{
    return foo;
}

This function returns a string reference. I think when calling it, a temporary reference variable will be generated, and then this temporary reference variable will be bound to foo, right?

No.

Still, this temporary reference variable will not be generated and foo will be returned directly?

Yes.

For example, the following assignment statement:

string foo;
string s = Func(foo);

is equivalent to the following two sentences:

string& temp = foo; //一个临时的引用变量绑定到foo上
string s = temp;

Do I understand correctly?

Yes

Returning a reference will actually generate a temporary reference variable (the size of a pointer), but using a reference avoids copying the object, right? If it's not correct, please point out where I understand it incorrectly. . .

Yes

But what if a temporary reference variable is returned and the address of it can be taken? For example:

cout << &Func(foo);

This temporary variable should be an rvalue, right?

is obviously an lvalue. Your Func returns string&, which is string&. It is an rvalue only when string&& is returned. And regardless of the lvalue or rightvalue, the address can be taken.

Are all operations on this temporary reference variable operations on the object it refers to?

Yes

Or this temporary reference variable will not be generated at all, and all operations are performed on the object returned by the function? - - I'm confused. Please point out my mistake! ! !

The author’s understanding of the question is basically correct, except for the address above

PHPzhong

First quote from the first floor

A reference refers to defining a variable as an alias for another variable. Access by reference is direct access to the referenced variable.

This sentence is very correct. A reference is actually an "alias". Anywhere a reference is used, it can be directly replaced by the object referenced by the reference. For example,

int i = 1;
int& r = i;
auto a = r;

So what type is a? It is int, not int&, which can be understood as directly replacing the r on the right with i. This is the so-called "alias". Where the reference is used, it is directly replaced by the referenced variable. This is the case in most cases. There is one exception, and that is the most "honest" decltype in C++. If you give it a reference, it becomes a reference, and if you give it an array, it becomes an array, without turning into a corresponding pointer.

decltype(r) n = r;

What type is n? Of course it's int&. The question arises, how to bind an int reference to an int reference? ? ? Of course not, just replace the r on the right side of the equal sign with i, and that will do the trick.
As for taking a pointer to a reference, isn’t it just taking a pointer to a variable by exchanging the reference?
Of course the above is a series of semantic analysis to help you understand what a quote is.

Then let’s look at the underlying implementation.
Is the so-called temporary reference variable a variable? The variable here refers to whether it takes up memory space?
Many C++ books say that references are actually implemented through pointers at the bottom level. I have not studied this carefully. If I want to know whether this is really the case, I am afraid I have to disassemble it. Now outside, without a computer, you can’t really “do experiments”.
But if you were a compiler writer, what would you do? If it were me, then I would directly use the "replace" function of the text editor to replace it with i wherever r is used. (Of course, it is definitely not as simple and crude as I described...) In this way, there is no problem of variables and invariants at all, because r has "disappeared". . .
In this case, do you still need to worry about whether there is a so-called reference variable?
So how do you explain the return value of that function? The questioner is trying to make things difficult for the compiler by asking it to create a temporary reference variable for you! Of course, the compiler is very smart. It will not create a temporary reference variable and then perform initialization assignment. It will directly use the return value of the function to initialize the defined reference variable, which is a temporarily created reference type. Function return value, this transition variable is simply superfluous. (And a closer look at this will involve function return value optimization and the emergence of rvalue references in c++11)
So, is it really that simple? Of course it's not that simple, otherwise why is c++ complicated?
Consider another scenario, which is the call of virtual functions. We know that when a virtual function is called on a pointer to a base class, the specific function called is determined by the real type of the object pointed to by the runtime pointer. This process is called dynamic binding. One point to emphasize is that dynamic binding will only occur when such a call is made to a pointer , see the example below.

DerivedClass object;
BaseClass* p = &object;
p -> virtualFunc();
BaseClass b = object;
b.virtualFunc();

The previous call is dynamic binding and will call the virtual function in DerivedClass. In the latter, the assignment in the fourth line is truncated . The call in the fifth line will of course not be dynamically bound. It will directly call the virtual function in BaseClass, and this parsing process occurs during compilation. device. More essentially, a copy initialization of BaseClass actually occurred here. b is actually another object different from object, and there is no dynamic binding at all.
Having gone so far, what exactly do I want to say? If the questioner pays attention, he should know that references can also lead to dynamic binding. Just add a symbol to the above code:

BaseClass& c = object;
c.virtualFunc();

At this time, the virtual function call will be dynamically bound again. This is too similar to a pointer. How is it done? Or is it the so-called direct "replacement"? Of course it can be replaced directly here because I didn't call the function. If I call a function that accepts a base class reference and pass it an object of a derived class as a parameter, will the compiler go into the function code to make changes? So what if I need to call this function on a hundred different derived class objects? Then wouldn't the compiler have to create a hundred function instances and replace the references in them with real variables? At this time, the compiler also knows that it can't be clever anymore, so it has no choice but to change the reference into a pointer, but it changes it quietly without telling you, so you still think that you are passing the reference. . .
Having said all this, what I mean is that if the compiler can optimize, it will generally optimize it for you. It can create temporary variables for you without creating any more. If the compiler cannot optimize, it can only take a step back and use pointers, but quietly, it makes you feel that you are still using to reference .

Back to the main question, whether the previous temporary variable is created, or whether it will eat up the memory of a pointer, depends on the specific implementation and optimization of the compiler. You can create a pointer and operate the pointer pointed to. Object, if the optimizer analyzes it correctly, it should not create temporary variables, and put all operations directly on foo, instead of indirectly accessing it from the location pointed by the pointer. Of course, I am just talking nonsense. How to implement it actually requires disassembly.
As for the latter issue of lvalues ​​and rightvalues. My personal understanding is that the reference itself is not an object, not even a variable. No matter how the compiler is implemented at the bottom, it is not a variable for us, so the reference itself does not have the properties of an lvalue or rvalue. And more of them refer to the lvalue rvalue property of the bound object, lvalue reference binds lvalue, rvalue reference and const lvalue reference binds rvalue. So in the end, the so-called reference is an rvalue, which is true, but when we operate the reference, we are actually operating the object to which the reference is bound. Here, the lvalue is bound but it is an rvalue (temporarily created ) lvalue reference. The lvalue rvalue properties of the reference itself are different from the lvalue rvalue properties of the object it is bound to, and generally we do not discuss the lvalue rvalue properties of the reference itself, because this involves the underlying implementation of the reference , and the reference should be an abstraction for us. So in the end, taking the address of the so-called reference that is an rvalue itself is actually taking the address of the lvalue string bound to the reference.

The final point to point out is that when writing C++, the subject does not need to worry too much about the so-called memory of a reference variable. You only need to know that generally reference is faster than copying (basic data types are not, Generally refers to a larger class object), why bother thinking about how to get rid of the overhead of the reference. . .

巴扎黑

Personal rough understanding: References and pointers are two different things.

  • A reference refers to defining a variable as an alias for another variable. Access by reference is direct access to the referenced variable.

  • A pointer first opens up a space for itself, and then puts the address pointing to the variable into the opened space. Access through pointers requires indirect access to the pointed variable through the dereference operation.


Update

The C++ standard only stipulates what a reference looks like (usage), but does not specify how a reference should be implemented. Your question actually relates to how the specific compiler implements references. Normally, references are implemented through pointers, but they provide a syntax that is easier to understand than pointers and are optimized at compile time. References are similar to constant pointers.

1. References do not necessarily take up space

C++11 Standard § 8.3.2/4
It is unspecified whether or not a reference requires storage.

For example:

int i = 1;
int &ri = i;

The compiler will optimize during compilation. ri will not occupy space alone. Whenever you encounter ri during compilation, just replace it with i.

Another example of the topic:

string& Func(string& foo)
{
    return foo;
}

This function parameterfoo cannot be optimized out at compile time, so space will be allocated.

2. The operation on the reference is the operation on the referenced variable

For example:

string foo;
string s = Func(foo);

calls the operator= of the referenced variable. It’s the same as the questioner understands.

Another example:

string foo;
&Func(foo);  // 等价于 &foo

calls the operator& of the referenced variable.

References of references do not exist in C++. The Func function returns a reference. For reference variables, operator& will act on the original variable it refers to, and the type of the original variable foo is string, and string has no overloading operator&, so &Func(foo) returns the address of variable foo.

C++11 Standard § 8.3.2/5
There shall be no references to references, no arrays of references, and no pointers to references. ...

Reference

  • What is a reference?

  • How does a C++ reference look, memory-wise?

小葫芦

No temporary string objects will be generated during this period, some are just transfers of references (actually memory addresses)

We say that the reference is actually an address (or pointer) from the perspective of language implementation, but from the perspective of C++ semantics, the reference belongs to the alias of the original object, so the address is the address of the original object, or change In a word

&Func(foo)

The address taken is the address of foo (of course, in terms of implementation, it is the address value where this reference is stored)

The questioner can compare the following examples to understand
int a = 0
int& r = a
&r == &a

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template