(传统的,之前的 c++11 左值-)引用如何在 C++ 中工作

How do (traditional, prior c++11 lvalue-) references work in C++

指针很容易。有一些内存保存着一个地址。要获得(有意义的)值,取消引用 returns 地址指向的内存中包含的值。

引用以某种方式做一些类似的事情:它们将 "link" 保存到一个临时对象。但是当我分配或使用引用时会发生什么?

#include <iostream>
using namespace std;
int one(){
    return 1;
}
int main()
{
    const int &rone = one();
    cout << &rone << endl;
    return 0;
}

为什么这样做?这个地址是临时对象的地址吗?

sh-4.2# g++ -std=c++11 -o main *.cpp
sh-4.2# main
0x7fff9d2a4e74

这个地址指向哪里?如果它指向神奇的临时对象,为什么我不能只执行以下操作。 (我很清楚它是右值并且 & 只接受左值,但为什么呢?)

int main()
{
    cout << &one() << endl;
    return 0;
}

我的问题超出了 c++ 的编程方面。它在技术方向上更多地意味着 C++ 在内部是如何工作的。我主要尝试在移动语义的上下文中理解右值引用,这需要理解那些传统引用(我几乎从不使用)。

您可以将临时文件绑定到 rone,因为编译器知道您不会修改它。读取临时值是安全的,写入临时值则不然。

如果删除 const,代码将不再编译。

另一种理解引用的方式是别名。绑定引用为对象提供了另一个名称。在 C++11 之前,这两个片段完全相同:

int a = 5;
int &b = a;

对比

int b = 5;
int &a = b;

此后,ab都是同一个对象的名称。 (C++11 引入了一种不对称性,因为 decltype 将在返回的类型中包含引用)。

这与您的第一个示例没有任何不同。您将 rone 绑定为临时对象的名称,语言规则说这也会延长临时对象的生命周期。

在你的第二个例子中没有引用。地址运算符是与用于声明引用的符号不同的语言元素,尽管它们具有相同的拼写。这是非法的,因为语言定义说地址运算符不能应用于创建临时对象的表达式。

变量(对象)和引用是"names"(指针也是变量所以同样适用)。程序员使用它们来识别程序中的某些对象。请注意,引用不仅引用临时对象。他们可以引用任何有效的对象。

其他东西(即右值)没有名称,也没有指定的可访问性 "location" 它们的处理是编译器的工作。在 x86 中的示例中(取决于调用约定),小的 return 值通常通过某些处理器寄存器 returned。

考虑:

int one() { return 1; }
int main()
{
  one();
  return 0;
}

return 值在这里消失。 (如果函数可见,编译器可能根本不会调用该函数。) 如果您可以获取 return 值的地址,则它必须存储在某处(可以使用有效地址访问的位置)。

因此,为了能够获取由 one() 编辑的值 return 的地址,必须从寄存器 EAX 复制该值(当使用 cdecl 时它通常驻留的位置)到某个位置 "save" 由 c++ 程序(mer)访问。

如果您确实可以访问临时 "directly" 的地址会怎样?

int const * p = &one(); // would this return "EAX"?
int c = *(p + 1); // ??²

你会读第二行的寄存器EBX吗?或者 L1 或 L2 或 或 中的任何内存位置?这样的事情没有意义。

C++ 中唯一可访问的位置是堆栈内存和空闲存储区 ("heap"),如果您为 return 类型创建变量或常量引用,将使用其中之一。

int main()
{
  int const a = one();
  return 0;
}

与这里的第一个示例相比,情况并没有太大变化:a 分配了名称和内存,而函数的 return 值(仍然)具有(并且将始终具有)不是。 return 的值是 "computed" 到 EAX 中,a 通过将寄存器值复制到 a 的内存位置来使用该整数初始化。

int const & r = one();

这里差别不大。 return 值存储在 persistent/accessible 内存位置,现在您可以使用

获取它的地址
int const * p = &r;

这个地址指向哪里?

我不确定,但我不认为该标准指定了长时间临时文件的任何位置(如果我错了请纠正我)。您只需要知道:r 指的是一个有效对象,&r 是该对象的地址。 (您可能可以将地址与附近的一些堆栈变量地址进行比较,以确定它是否也可能在堆栈上。)

ra 之间有一点不同:值(存储在 r 所指的位置)不一定在堆栈上(尽管我认为它会最终在堆栈上),而上面的 a 肯定在堆栈上。

另一个不同之处在于对象。 (至少在理论上,尽管允许 c++ 编译器以某种方式进行优化,但以下段落中的 ab 在某种程度上是等效的)。

struct X { /*stuff*/ };
X foo() { return X{ /*.args.*/ }; }
// ...
X const a = foo(); // A
X const & b = foo(); // B

在行 B 中,bX 类型常量对象的名称。 (在幕后,编译器会提前为对象选择一些位置,然后 return foo 直接进入该内存以避免复制它。)

在行 A 中,a 将(在堆栈上并)从临时 return 由 foo 通过它的复制构造函数初始化。 (可以省略副本,优化编译器很可能会将临时文件直接构造为 a。)

变量名和引用是人类识别标记。变量/对象是可能存在多个引用的单个实例。