Is a C++ reference just syntactic sugar, or does it offer any speed ups in certain cases?
c++引用仅仅是语法上的糖,还是在某些情况下提供了加速功能?
For example, a call-by-pointer involves a copy anyway, and that seems to be true about a call-by-reference as well. The underlying mechanism appears to be the same.
例如,无论如何,指针调用都包含一个副本,这似乎也适用于引用调用。底层机制似乎是相同的。
Edit: After about six answers and many comments. I am still of the opinion references are just syntatic sugar. If people could answer in a straight yes or no, and if someone could do an accepted answer?
编辑:在大约六个答案和许多评论之后。我还是认为引用只是一种合成糖。如果人们可以直接回答“是”或“否”,如果有人能给出一个公认的答案?
58
References have stronger guarantees than pointers, so the compiler can optimize more aggressively. I've recently seen GCC inline multiple nested calls through function references perfectly, but not a single one through function pointers (because it couldn't prove that the pointer was always pointing at the same function).
引用比指针具有更强的保证,因此编译器可以更积极地进行优化。我最近看到GCC通过函数引用完美地嵌入多个嵌套调用,但不是通过函数指针(因为它不能证明指针总是指向同一个函数)。
If the reference ends up stored somewhere, it typically takes the same space as a pointer. That is not to say, again, that it will be used like a pointer : the compiler may well cut through it if it knows which object the reference was bound to.
如果引用最终被存储在某个地方,那么它通常以相同的空间作为指针。这并不是说,它将像指针一样使用:如果编译器知道引用绑定到哪个对象,它很可能会对它进行切割。
54
Assume reference as a pointer that:
假设引用作为指针:
Any attempt to use it will implicitly dereference it:
任何使用它的尝试都将隐式地撤销它:
int a = 5;
int &ra = a;
int *pa = &a;
ra = 6;
(*pa) = 6;
here as it looks in disassembly:
这里是拆解时的样子:
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
the assigning to the reference is the same thing from the compiler perspective as the assigning to a dereferenced pointer. There are no difference between them as you can see (we are not talking about compiler optimization right now) However as mentioned above, references can't be null and have stronger guarantees of what they contains.
从编译器的角度来看,对引用的赋值与对取消引用指针的赋值是相同的。您可以看到,它们之间没有区别(我们现在讨论的不是编译器优化),但是正如上面提到的,引用不能为空,并且对它们包含的内容有更强的保证。
As for me, I prefer using references as long as I don't need nullptr
as a valid value, values that should be repointed or values of different types to be passed into (e.g. pointer to interface type).
至于我,我更喜欢使用引用,只要我不需要nullptr作为一个有效值,值应该被重新指向或传递到不同类型的值(例如,指向接口类型的指针)。
19
The compiler cannot assume a pointer is non-null; when optimizing code, it has to either prove the pointer is non-null, or emit a program that accounts for the possibility that it is null (in a context where that would be well-defined).
编译器不能假定一个指针是非空的;在优化代码时,它必须证明指针是非空的,或者发出一个程序来解释它为空的可能性(在定义良好的上下文中)。
Similarly, the compiler cannot assume the pointer never changes value. (nor can it assume the pointer points to a valid object, although I'm having trouble imagining a case where that would matter in a well-defined context)
类似地,编译器不能假设指针永远不会改变值。(它也不能假定指针指向一个有效的对象,尽管我很难想象在一个定义良好的上下文中,这一点很重要)
On the other hand, assuming that references are implemented as pointers, the compiler is still allowed to assume it is non-null, never changes where it points, and points to a valid object.
另一方面,假设引用被实现为指针,编译器仍然可以假设它是非空的,从不改变它指向的位置,并指向一个有效的对象。
10
References differ from pointers in that there are things you cannot do to a reference and have it be defined behavior.
引用与指针的不同之处在于,您不能对引用做一些事情,并将其定义为行为。
You cannot take the address of a reference, but only what is referred to. You cannot modify a reference once it is created.
不能取引用地址,只能取引用地址。创建引用后,不能修改引用。
A T&
and a T*const
(note that const
applies to the pointer, not the pointed-to, there) are relatively similar. Taking the address of an actual const
value and modifying it is undefined behavior, as is modifying (any storage that it uses directly) a reference.
一个t&t *const(注意const适用于指针,而不是指向)是相对相似的。获取实际const值的地址并修改它是未定义的行为,就像修改(它直接使用的任何存储)引用一样。
Now, in practice, you can get a the storage of a reference:
现在,在实践中,你可以得到一个参考文献的存储:
struct foo {
int& x;
};
sizeof(foo)
will almost certainly equal sizeof(int*)
. But the compiler is free to neglect the possibility that someone directly accessing the bytes of foo
could actually change the value referred to. This permits the compiler to read the reference "pointer" implementation once, and then never read it again. If we had struct foo{ int* x; }
the compiler would have to prove each time it did a *f.x
that the pointer value had not changed.
sizeof(foo)几乎肯定等于sizeof(int*)。但是编译器可以自由地忽略直接访问foo字节的人实际上可以更改所引用的值的可能性。这允许编译器读取一次引用的“指针”实现,然后再也不读取它。如果我们有struct foo{int* x;编译器每次做*f时都要证明。指针的值没有改变。
If you had struct foo{ int*const x; }
is again starts behaving reference-like in its immutability (modifying something that was declared const
is UB).
如果你有struct foo{int*const x;}再次开始以其不可变性为参照(修改声明为const的内容为UB)。
A trick that I'm not aware of any compiler writers using is to compress reference-capture in a lambda.
我不知道编译器作者使用的一个技巧是压缩lambda中的引用捕获。
If you have a lambda that captures data by reference, instead of capturing each value via a pointer, it could capture only the stack frame pointer. The offsets to each local variable are compile-time constants off the stack frame pointer.
如果有一个lambda根据引用捕获数据,而不是通过指针捕获每个值,那么它只能捕获堆栈帧指针。每个局部变量的偏移量都是堆栈帧指针之外的编译时常量。
The exception is references captured by reference, which under a defect report to C++ must remain valid even if the reference variable goes out of scope. So those have to be captured by pseudo-pointer.
异常是引用捕获的引用,如果引用变量超出范围,则在缺陷报告中对c++必须保持有效。这些必须被伪指针捕获。
For a concrete example (if a toy one):
具体例子(如玩具):
void part( std::vector& v, int left, int right ) {
std::function op = [&](int y){return yright;};
std::partition( begin(v), end(v), op );
}
the lambda above could capture only the stack frame pointer, and know where left
and right
are relative to it, reducing it size, instead of capturing two int
s by (basically pointer) reference.
上面的lambda只捕获堆栈帧指针,并知道左和右相对于它的位置,从而减小它的大小,而不是通过(基本上是指针)引用捕获两个ints。
Here we have references implied by [&]
whose existence is eliminated easier than if they where pointers captured by value:
这里我们有[&]所暗示的引用,它们的存在比value捕获的指针更容易消除:
void part( std::vector& v, int left, int right ) {
int* pleft=&left;
int* pright=&right;
std::function op = [=](int y){return y<*pleft && y>*pright;};
std::partition( begin(v), end(v), op );
}
There are a few other differences between references and pointers.
引用和指针之间还有一些其他的区别。
A reference can extend the lifetime of a temporary.
引用可以延长临时引用的生命周期。
This is used heavily in for(:)
loops. Both the definition of the for(:)
loop relies on reference lifetime extension to avoid needless copies, and users of for(:)
loops can use auto&&
to automatically deduce the lightest weight way to wrap the iterated objects.
这在for(:)循环中被大量使用。for(:)循环的定义依赖于引用的生命周期扩展,以避免不必要的复制,而for(:)循环的用户可以使用auto&&以自动推断出最轻的方式来包装迭代对象。
struct big { int data[1<<10]; };
std::array arr;
arr get_arr();
for (auto&& b : get_arr()) {
}
here reference lifetime extension carefully prevents needless copies from ever occuring. 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 big
elements by-value (say, an input iterator range), again no needless copies are done.
在这里,引用生命期扩展小心地防止不必要的拷贝发生。如果我们更改make_arr以返回一个arr const&它将继续工作,没有任何副本。如果我们更改get_arr以返回一个容器,该容器返回一个按值大小的大元素(例如,一个输入迭代器范围),那么同样没有必要的复制。
This is in a sense syntactic sugar, but it allows the same construct to be optimal in many cases without having to micro-optimize based on how things are returned or iterated over.
从某种意义上说,这是句法上的糖,但在许多情况下,它允许相同的结构是最优的,而不必根据返回或迭代的方式进行微优化。
Similarly, forwarding references allow data to be treated as a const, non-const, lvalue or rvalue intelligently. Temporaries are marked as temporaries, data that users have no further need for is marked as temporary, data that will persist is marked as being an lvalue reference.
同样,转发引用允许数据被智能地视为const、non-const、lvalue或rvalue。临时数据被标记为临时数据,用户不再需要的数据被标记为临时数据,持久的数据被标记为lvalue引用。
The advantage references have over non-references here is that you can form a rvalue reference to a temporary, and you cannot form a pointer to that temporary without passing it through an rvalue reference-to-lvalue reference conversion.
与非引用相比,这里的优点是可以形成对临时引用的rvalue引用,而且如果不通过rvalue -to-lvalue引用转换,就不能形成指向临时引用的指针。
8
No
没有
References are not just a syntactic difference; they also have different semantics:
引用不仅仅是语法上的差异;它们还有不同的语义:
nullptr
(a sentinel value).auto const&
or auto&&
.Thus, at the language level, a reference is an entity of its own. The rest are implementation details.
因此,在语言水平上,引用是它自己的实体。其余是实现细节。
7
There used to be efficiency advantages because references are easier for the compiler to optimize. However, modern compilers have gotten so good at it that there is no longer any advantage.
以前有效率优势,因为编译器更容易优化引用。然而,现代的编译器在这方面做得太好了,以至于不再有任何优势。
One huge advantage references have over pointers is that a reference can refer to a value in a register, while pointers can only point at blocks of memory. Take the address of something which would have been in a register, and you would force the compiler to put that value into a normal memory location instead. This can create tremendous benefits in tight loops.
与指针相比,引用的一个巨大优势是引用可以引用寄存器中的值,而指针只能指向内存块。取寄存器中某个值的地址,然后强制编译器将该值放入一个普通的内存位置。这可以在紧密的循环中产生巨大的好处。
However, modern compilers are so good that they now recognize a pointer that could have been a reference for all intents and purposes, and treat it exactly the same as if it were a reference. This can cause rather intriguing results in a debugger, where you can have a statement such as int* p = &x
, ask the debugger to print the value of p
, only to have it say something along the lines of "p cannot be printed" because x
was actually in a register, and the compiler was treating *p
as a reference to x
! In this case, there literally is no value for p
然而,现代的编译器是如此的优秀,以至于它们现在能够识别出一个指针,它本来可以作为所有意图和目的的引用,并将它当作引用来处理。这可能会导致相当有趣的结果在一个调试器,你可以有一个声明如int * p = x,让调试器打印p的价值,只是说一些的“p不能打印”,因为x是一个寄存器中,编译器是治疗x * p作为参考!在这种情况下,字面上没有p的值
(However, if you tried to do pointer arithmetic on p
, you would then force the compiler to no longer optimize the pointer to act like a reference does, and everything would slow down)
(但是,如果您尝试在p上执行指针运算,那么您将迫使编译器不再像引用那样优化指针,一切都会变慢)
1
8.3.2 References [dcl.ref]
8.3.2引用(dcl.ref)
A reference can be thought of as a name of an object
引用可以被认为是对象的名称
which is different from pointers which is a variable (unlike reference) that holds the address of a memory location of an Object**. The type of this variable is pointer to Object.
它与指针不同,指针是一个变量(不像引用),它保存对象的内存地址**。该变量的类型是指向对象的指针。
Internally Reference may be implemented as pointer, but standard never guaranteed so.
内部引用可以实现为指针,但标准从来没有这样保证。
So to answer your question: C++Reference are not syntactic sugar to pointers. And whether it provides any speedup has already been answered in depth.
因此,要回答你的问题:c++引用不是指针的语法糖。它是否提供任何加速已经得到了深入的回答。
****** Object here it means any instance that has a memory address. Even pointers are Objects and so are functions (and thus we have nested pointers and function pointers). In similar sense, we do not have pointers to reference as they are not instantiated.
*****对象,它表示有一个内存地址的任何实例。即使指针是对象,函数也是对象(因此我们有嵌套的指针和函数指针)。在类似的意义上,我们没有要引用的指针,因为它们没有被实例化。