请注意,这是一个语言律师的问题。我知道“变量”的常见含义,但我想了解标准中对“变量”的具体定义。我正在编写一些教学材料,想使用正确的定义。
变量通常被定义为“命名对象”,或者更准确地说是“已声明的对象”(因为变量可以未命名,但总是被声明)。
但还有第二种观点认为变量是一个编译时概念。根据这种定义,源代码中的一个声明始终对应一个变量,即使在运行时从该变量创建了多个对象。
哪一个是正确的?
这里有一个例子来说明这一点。假设你有
void foo(bool a)
{
int x = 42;
if (a)
foo(false);
}
当你调用时foo(true)
,两个 get 实例int x = 42;
同时存在。
它们显然是两个不同的对象,但从技术上讲它们是不同的变量还是相同的变量?
如果变量是编译时的东西,那么两者就是同一个变量。
第二个定义从表面上看是错误的,但这里有一个 C++委员会成员认为它是正确的。
尽管我的观点与所引用的观点并非完全独立,但我还是非常同意:标准中“变量”一词的含义更适合解释为“某些形式声明的抽象主体”,而不是“被视为具有特定类型的内存空间”。标准经常需要讨论变量的名称、声明变量的作用域、两个声明(可能在不同的翻译单元中)是否声明了同一个变量、一个变量是否是引用等等,而这些显然与单个翻译时构造有关,而不是与执行期间存在的任何事物有关。最后一点最有力的证据是,即使变量是函数中从未调用的非静态局部变量,所有关于变量可用于常量表达式的规则都适用。
另一方面,“对象”一词的使用方式主要与“真实数据”概念相对应:它具有值和地址,并且可以处于或超出生命周期。这是有道理的,因为标准必须支持所有通过
new
(放置或其他方式)创建的对象上的运行时操作,而这些对象显然不是变量。不幸的是,标准在这方面并不完全一致,过于依赖读者对意图的直觉理解,例如“变量由对象的声明[…]引入。”([basic.pre]/7)和“具有自动存储期限的块变量[…]按声明顺序初始化”([stmt.dcl]/2)。目前已经取得了一些进展(C++17 不再将
*this
“实体”称为可以通过名称查找或其他方式找到的实体),并且还有更多进展即将到来(Reflection 论文正在做进一步的清理工作,因为想要描述其^^x
含义)。C++ 标准(不完整地)指定了执行您编写的代码的抽象机器的行为。
将其映射到实际硬件完全取决于编译器;只要生成的程序的行为“就像”是抽象机器一样,那么该程序就是符合要求的。
这允许编译器执行诸如完全消除对象之类的操作,只要程序的行为“如同”该对象存在一样。程序运行的硬件上的实际位不受限制,除非在某些情况下与预期的位不一致是非常不切实际的。
这种“as-if”选项通常在编译器几乎完全了解对象可能用途的情况下使用。例如,没有人检查指向局部变量的指针。标准规定某些操作是未定义行为(例如,在 C++ 中手动遍历堆栈就要求未定义行为);这意味着它可以证明没有人“合法地”访问过某个变量,因此可以消除该变量的存在(或将其更改为其他变量)。
在你的示例程序中,没有人访问
x
,所以它在运行时不必存在。事实上,如果你检查优化编译器生成的代码,你会发现它确实缺失了。