C ++ links is just syntactic sugar?

Is the C ++ reference just syntactic sugar, or does it offer any speedups in certain cases?

For example, a pointer call in any case includes a copy, and this seems to be true for a link call as well. The underlying mechanism seems to be the same.

Edit: after six answers and a lot of comments. I still think links are just syntactic sugar. If people could answer directly or not, and if someone could accept the accepted answer?

+63
c ++
Jul 07 '15 at 8:00
source share
7 answers

Links have stronger guarantees than pointers, so the compiler can optimize more aggressively. I recently saw that GCC built in several nested calls through function references perfectly, but not one through function pointers (because it could not prove that the pointer always pointed to the same function).

If the link ends somewhere, it usually takes the same place as the pointer. This does not mean that it will be used as a pointer: the compiler can cut it through if it knows to which object the link is bound.

+58
Jul 07 '15 at 8:10
source share

Suppose the link is listed as:

  • Cannot be null
  • You cannot override another object after initialization.
  • Any attempt to use it implicitly casts it:

    int a = 5; int &ra = a; int *pa = &a; ra = 6; (*pa) = 6; 

here what it looks like when disassembling:

  int a = 5; 00ED534E mov dword ptr [a],5 int &ra = a; 00ED5355 lea eax,[a] 00ED5358 mov dword ptr [ra],eax int *pa = &a; 00ED535B lea eax,[a] 00ED535E mov dword ptr [pa],eax ra = 6; 00ED5361 mov eax,dword ptr [ra] 00ED5364 mov dword ptr [eax],6 (*pa) = 6; 00ED536A mov eax,dword ptr [pa] 00ED536D mov dword ptr [eax],6 

Assigning a reference is the same from the point of view of the compiler as assigning a dereference pointer. There is no difference between them, as you can see (we are not talking about compiler optimization right now) However, as mentioned above, links cannot be null and have stronger guarantees that they contain.

As for me, I prefer to use references if I don't need nullptr as a valid value, values ​​that need to be rewritten, or values ​​of different types that need to be passed (for example, a pointer to an interface type).

+54
Jul 07 '15 at 9:25
source share

The compiler cannot assume that the pointer is non-zero; when optimizing the code, it must either prove that the pointer is not null or emits a program that takes into account the probability that it is zero (in the context where it would be clearly defined).

Likewise, the compiler cannot assume that a pointer never changes a value. (and he cannot assume that the pointer points to a valid object, although I find it hard to imagine a case where it matters in a well-defined context)

On the other hand, if we assume that the links are implemented as pointers, the compiler is still allowed to assume that it is not equal to zero, never changes where it points, and points to a valid object.

+19
Jul 07 '15 at 8:08
source share

Links differ from pointers in that there are things that you cannot do for a link and determine its behavior.

You cannot take the link address, but only what is mentioned. You cannot change the link after creating it.

A T& and a T*const (note that const applies to the pointer, not the specified one) relative to. Taking the address of the actual value of const and changing it, this behavior is undefined, since it modifies (any storage that it uses directly) the link.

Now in practice, you can get the help repository:

 struct foo { int& x; }; 

sizeof(foo) almost certainly be equal to sizeof(int*) . But the compiler may neglect the possibility that someone directly accessing the foo bytes may actually change the value mentioned. This allows the compiler to immediately read the "pointer" link and then never read it again. If we had struct foo{ int* x; } struct foo{ int* x; } , then the compiler would have to prove every time he did *fx that the value of the pointer did not change.

If you had struct foo{ int*const x; } struct foo{ int*const x; } , he again begins to maintain the link, as in his immutability (a change of what was declared const - UB).




The trick I don't know about how to use compilers is to compress the binding binding in lambda.

If you have a lambda that captures data by reference, instead of capturing each value with a pointer, it can only capture the pointer to the stack frame. The offsets of each local variable are compile-time constants from the stack frame pointer.

An exception is links taken by the link, which in the defect report for C ++ remain valid even if the reference variable is outside the scope. Thus, they must be captured by a pseudo-pointer.

For a specific example (if there is a toy):

 void part( std::vector<int>& v, int left, int right ) { std::function<bool(int)> op = [&](int y){return y<left && y>right;}; std::partition( begin(v), end(v), op ); } 

lambda above can only capture the stack frame pointer and know where left and right refer to it, decreasing its size, instead of writing two int references to (basically a pointer).

Here we have references, implied [&] , whose existence is eliminated more easily than if they were, where the pointers were captured by value:

 void part( std::vector<int>& v, int left, int right ) { int* pleft=&left; int* pright=&right; std::function<bool(int)> op = [=](int y){return y<*pleft && y>*pright;}; std::partition( begin(v), end(v), op ); } 



There are several other differences between links and pointers.

Link can extend the life time of a temporary.

This is heavily used in for(:) loops. Both for(:) loop definitions rely on extending the link life cycle to avoid unnecessary copies, and for(:) loop users can use auto&& to automatically display the easiest way to wrap completed objects.

 struct big { int data[1<<10]; }; std::array<big, 100> arr; arr get_arr(); for (auto&& b : get_arr()) { } 

here the reference life extension carefully prevents the appearance of extra copies. If we change make_arr to return a arr const& , it continues to work without any copies. If we change get_arr to return a container that returns the big value of the elements (for example, the input range of the iterator), extra copies are not executed again.

It is in a sense syntactic sugar, but it allows in many cases to maintain the same design without the need for micro-optimization, depending on how things come back or are repeated.




Similarly, forwarding links allow you to process data as const, non-const, lvalue or rvalue intelligently. Timestamps are marked as temporary, data that users no longer need is marked as temporary, data to be saved is marked as lvalue reference values.

Links to benefits have links to non-links, so you can create an rvalue link for a temporary one, and you cannot create a pointer to this temporary place without passing it through the link conversion of the link link to lvalue.

+10
Jul 07 '15 at 21:19
source share

No




Links are not just syntactic difference; they also have different semantics:

  • A link always pseudonizes an existing object, as opposed to a pointer, which can be nullptr (control value).
  • A link cannot be reinstalled; it always points to the same object throughout its entire service life.
  • A link can extend the lifetime of an object; see binding to auto const& or auto&& .

Thus, at the language level, the link is independent. The remaining implementation details.

+8
Jul 08 '15 at 8:02
source share

There used to be performance benefits because links are easier to optimize for the compiler. However, modern compilers have become so good at it that there are no more advantages.

In one huge value, there are advantages over pointers, since a link can refer to a value in a register, while pointers can only point to memory blocks. Take the address of something that would be in a register, and you would force the compiler to put this value in a normal memory location. This can create tremendous benefits in hard cycles.

However, modern compilers are so good that now they recognize a pointer that could be a link for all purposes and goals, and treat it exactly as if it were a link. This can lead to quite intriguing results in the debugger, where you can have an operator such as int* p = &x , ask the debugger to print the value of p , only to say something along the lines of “p cannot be printed”, because x was actually in the register, and the compiler treated *p as a reference to x ! In this case, there is literally no value for p

(However, if you tried to perform arithmetic on a pointer to p , you would force the compiler to no longer optimize the pointer to act as a reference, and everything would slow down)

+7
Jul 07 '15 at 17:15
source share

8.3.2 Links [dcl.ref]

The link can be considered as the name of the object

which differs from pointers , which is a variable (as opposed to a reference) that contains the address of the object’s memory ** . The type of this variable is a pointer to an Object.

An internal reference can be implemented as a pointer, but the standard is never guaranteed.

So, to answer your question: C ++ Reference are not syntactic sugar for pointers. And regardless of whether it provides any kind of acceleration, an answer has already been given in depth.

****** The object here means any instance that has a memory address. Even pointers are objects as well as functions (and therefore we have nested pointers and function pointers). In a similar sense, we do not have pointers to a link, since they are not created.

+1
Jul 08 '15 at 3:53 on
source share



All Articles