为什么字符串文字是l值,而所有其他文字都是r值
Why are string literals l-value while all other literals are r-value?
C++03 5.1主要表达式§2表示:
文字是主要的表达方式。它的类型取决于它的形式(2.13)。字符串文字是一个左值;所有其他文字都是右值。
类似地,C99 6.5.1§4说:
字符串文字是主要表达式。它是一个类型如6.4.5所述的左值。
这背后的理由是什么?
据我所知,字符串文字是对象,而所有其他文字都不是。l值总是指一个对象。
但问题是,为什么字符串文字是对象,而所有其他文字都不是?在我看来,这个理由更像是鸡蛋或鸡肉的问题。
我知道这个问题的答案可能与硬件架构有关,而不是与C/C++作为编程语言有关,尽管如此,我还是希望听到同样的答案。
字符串文字是具有数组类型的文字,在C中,数组类型除了作为左值之外,不可能存在于表达式中。字符串文本可以被指定为具有指向字符串"contents"的指针类型(而不是通常衰减为指针的数组类型),但这会使它们用处不大;特别是sizeof
运算符不能应用于它们。
注意C99引入了复合文字,它们也是左值,因此将文字作为左值不再是一个特殊的例外;这更接近于成为常态。
字符串文字是数组-具有固有不可预测大小的对象(即用户定义的,可能是大大小的)。在一般情况下,除了作为内存中的对象(即lvalues
)之外,根本没有其他方法来表示这些文字。在C99中,这也适用于复合文字,它们也是lvalues
。
任何试图在语言级别上人为地隐藏字符串文字是lvalues
的事实都会产生大量完全不必要的困难,因为用指针指向字符串文字的能力以及作为数组访问它的能力在很大程度上取决于它的左值在语言级别是可见的。
同时,标量类型的文字具有固定的编译时大小。同时,这种文字很可能直接嵌入到给定硬件体系结构上的机器命令中。例如,当您编写类似i = i * 5 + 2
的东西时,文字值5
和2
将成为生成的机器代码的显式(甚至隐式)部分。它们不存在,也不需要作为数据存储中的独立位置存在。在数据存储器中存储值5
和2
根本没有意义。
同样值得注意的是,在许多(如果不是大多数或所有)硬件体系结构上,浮点字面值实际上被实现为"隐藏"lvalues
(即使该语言没有将其公开)。在x86等平台上,浮点组中的机器命令不支持嵌入的立即数操作数。这意味着编译器必须将几乎每个浮点文字存储在数据内存中(并从中读取)。例如,当你写类似i = i * 5.5 + 2.1
的东西时,它会被翻译成类似的东西
const double unnamed_double_5_5 = 5.5;
const double unnamed_double_2_1 = 2.1;
i = i * unnamed_double_5_5 + unnamed_double_2_1;
换句话说,floating-point literals
经常在内部变成"非官方的"lvalues
。然而,语言规范并没有试图公开这个实现细节,这是完全合理的。在语言层面上,arithmetic literals
比rvalues
更有意义。
我猜最初的动机主要是一个务实的动机:字符串文字必须存在于内存中并具有地址。字符串的类型literal是一种数组类型(在C中为char[]
,在C++中为char const[]
),并且在大多数上下文中,数组类型转换为指针。语言可以已经找到了其他方法来定义它(例如,字符串文字可能指针类型,带有关于它的特殊规则指向),但仅仅使文字成为左值可能是定义具体需要什么的最简单方法。
C++中的lvalue
并不总是引用对象。它也可以指函数。此外,对象不必由lvalues
引用。它们可以由rvalues
引用,包括用于数组(在C++和C中)。然而,在旧的C89中,数组到指针的转换不适用于rvalues
数组。
现在,rvalue
表示没有、有限或即将过期的寿命。然而,字符串文字适用于整个程序。
所以string literals
就是lvalues
是完全正确的。
答案和评论中有很多有价值的信息。有几点值得强调。
数组可以是右值。更多信息可以在这里和这里找到。例如,以下代码涉及一个右值数组:
template <typename T>
using alias = T;
int main() {
return alias<int[]>{23, 37, 53}[1];
}
因此,将字符串文字作为数组并使其成为lvalue是不好的。
最好记住字符串文字在程序的整个生命周期中都是有效的。尽管值类别不是生存期,但理解为什么字符串文字是基于其生存期的lvalue是有意义的。
就像许多关于值类别的讨论一样,字符串文字是lvalue在很大程度上是由语用考虑驱动的,即到目前为止在语言发展中发生了什么,以及从我们当时的立场来看,什么是最好的。
- 在执行其他功能的同时播放动画(LED矩阵和Arduino/ESP8266)
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- constexpr 函数中的非文字(通过 std::is_constant_evaluated)
- 我可以使用条件运算符初始化C风格的字符串文字吗
- GL_SHADERSTORAGE_BUFFER位置是否与其他着色器位置冲突
- 为什么我不能在 C++ 中的特定函数重载中调用同一函数的任何其他重载?
- 在其他文件中创建类时在 c++ 项目中不起作用
- 类与私有变量的其他类之间的线程安全性
- 将--whole archive链接器选项与CMake和具有其他库依赖项的库一起使用
- GlobalAlloc而不是其他分配方法
- 如何使用字符串文字作为宏参数
- C++从其他 constexpr 创建 lambda 不能按顺序执行 Constexpr
- 有没有办法从非C/C++文件中读取C++原始字符串文字的内容
- 构造<int>具有 2 个字符串文字的向量
- 断言中的Fold表达式在某些计算机上编译,但在其他计算机上不编译
- Visual Studio(或任何其他工具)能否将地址解释为调用堆栈(boost上下文)的开头
- 结构和双指针隐藏在其他结构中,多层混淆
- 将数字打印成文字
- 为什么字符串文字是l值,而所有其他文字都是r值