C++;引用-它们只是语法上的糖吗? 是C++的引用,只是句法糖,还是在某些情况下提供了加速?< /P>
例如,指针调用无论如何都涉及副本,而引用调用似乎也是如此。潜在的机制似乎是相同的C++;引用-它们只是语法上的糖吗? 是C++的引用,只是句法糖,还是在某些情况下提供了加速?< /P>,c++,C++,例如,指针调用无论如何都涉及副本,而引用调用似乎也是如此。潜在的机制似乎是相同的 编辑:在大约六个答案和许多评论之后。我仍然认为参考文献只是合成糖。如果人们可以直接回答“是”或“否”,如果有人可以做一个可接受的答案?编译器不能假定指针为非空;在优化代码时,它必须证明指针是非空的,或者发出一个程序来解释它为空的可能性(在定义良好的上下文中) 类似地,编译器不能假定指针从不更改值。(它也不能假设指针指向有效对象,尽管我很难想象在定义良好的上下文中这一点会起作用) 另一方面,假设引用是作为指针实现的,
编辑:在大约六个答案和许多评论之后。我仍然认为参考文献只是合成糖。如果人们可以直接回答“是”或“否”,如果有人可以做一个可接受的答案?编译器不能假定指针为非空;在优化代码时,它必须证明指针是非空的,或者发出一个程序来解释它为空的可能性(在定义良好的上下文中) 类似地,编译器不能假定指针从不更改值。(它也不能假设指针指向有效对象,尽管我很难想象在定义良好的上下文中这一点会起作用)
另一方面,假设引用是作为指针实现的,编译器仍然可以假定它是非空的,从不更改它指向的位置,并且指向有效的对象。引用比指针有更强的保证,因此编译器可以更积极地优化。我最近看到GCC通过函数引用完美地内联了多个嵌套调用,但没有一个通过函数指针内联调用(因为它无法证明指针总是指向同一个函数)
如果引用最终存储在某个地方,它通常会占用与指针相同的空间。这并不是说,它会像指针一样使用:如果编译器知道引用绑定到哪个对象,它可能会很好地将其截断。假设引用是一个指针:
int a = 5;
int &ra = a;
int *pa = &a;
ra = 6;
(*pa) = 6;
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
从编译器的角度来看,对引用的赋值与对取消引用的指针的赋值是相同的。正如您所看到的,它们之间没有区别(我们现在不讨论编译器优化)
但是如上所述,引用不能为null,并且对它们所包含的内容有更强的保证
就我而言,我更喜欢使用引用,只要我不需要将
nullptr
作为有效值、应该重新输入的值或要传递到其中的不同类型的值(例如指向接口类型的指针)。过去有效率优势,因为引用更易于编译器优化。然而,现代编译器已经非常擅长于此,不再有任何优势
与指针相比,引用的一个巨大优势是引用可以引用寄存器中的值,而指针只能指向内存块。以寄存器中的某个内容的地址为例,您将强制编译器将该值放入正常内存位置。这可以在紧密的循环中创造巨大的好处
然而,现代编译器非常优秀,现在它们可以识别出一个指针,该指针可以是所有意图和目的的引用,并将其视为完全相同的引用。这可能会在调试器中产生相当有趣的结果,在调试器中,您可以有一条语句,例如int*p=&x
,要求调试器打印p
的值,但却让调试器说“p无法打印”,因为x
实际上在寄存器中,编译器将*p
视为对x
的引用!在这种情况下,p
(但是,如果您尝试在
p
上执行指针算术,则会迫使编译器不再优化指针,使其像引用那样运行,并且一切都会变慢)引用与指针的不同之处在于,您无法对引用执行某些操作,并且必须定义引用的行为
您不能获取引用的地址,而只能获取引用的内容。一旦创建引用,就不能修改它
AT&
和AT*const
(注意,const
适用于指针,而不是指向的指针)相对类似。获取实际const
值的地址并对其进行修改是未定义的行为,修改(它直接使用的任何存储)引用也是未定义的行为
现在,在实践中,您可以从引用的存储中获得以下信息:
struct foo {
int& x;
};
sizeof(foo)
几乎肯定等于sizeof(int*)
。但是编译器可以忽略直接访问foo
字节的人实际上可能会更改引用的值的可能性。这允许编译器读取引用“指针”实现一次,然后再也不读取它。如果我们有struct foo{int*x;}
,编译器必须在每次执行*f.x
时证明指针值没有更改
如果您有struct foo{int*const x;}
is在其不变性(修改声明为const
is UB的内容)中再次开始执行类似于引用的操作
我不知道编译器编写者使用的一个技巧是在lambda中压缩引用捕获 如果lambda通过引用捕获数据,而不是通过指针捕获每个值,那么它只能捕获堆栈帧指针。每个局部变量的偏移量都是堆栈帧指针之外的编译时常量
异常是引用捕获的引用,即使引用变量超出范围,缺陷报告到C++的引用必须保持有效。所以这些必须由伪指针捕获
具体示例(如果是玩具):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 );
}
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 );
}
struct big { int data[1<<10]; };
std::array<big, 100> arr;
arr get_arr();
for (auto&& b : get_arr()) {
}