【问题标题】:Using a temporary array as an lvalue使用临时数组作为左值
【发布时间】:2015-10-16 01:14:23
【问题描述】:

这个程序格式不正确:

struct X { int i; };

int main() {
    (X { }).i = 1;
}

i,临时X { } 的子对象,不能用作左值,因为X { } 是右值。

但是,这可以使用 GCC 5.2.1 和 -Wall 静默编译:

using Y = int[10];

int main() {
    (Y { })[0] = 1;
}

如果编译器是正确的,那么这一次,(Y { })的第0个元素,也就是(Y { })的子对象,可以被当作左值处理。

我的问题是:

  1. 第二个程序格式不正确吗?
  2. 为什么(不),即使两个程序似乎都将临时对象的子对象视为左值?

【问题讨论】:

标签: c++ c++11 language-lawyer c++14


【解决方案1】:

我认为第二种情况不正确,如果我正确阅读defect report 1213,它会说:

因为下标操作被定义为间接通过 指针值,下标运算符应用于 xvalue 的结果 数组是左值,而不是 xvalue。这可能会让一些人感到惊讶。

解决方案是对草案 C++ 标准第 5.2.1 节 [expr.sub] 的以下更改(添加了粗体部分并删除了删除线):

后缀表达式后跟方括号中的表达式是 后缀表达式。其中一个表达式应具有类型 “T 数组”或“指向 T”的指针,另一个应为无作用域枚举或整数类型。结果是类型的 左值 “T。”类型“T”应是完全定义的对象类型。 62 表达式 E1[E2] 与 *((E1)+(E2)) 相同(根据定义)[注: 有关 * 和 + 的详细信息,请参见 5.3 [expr.unary] 和 5.7 [expr.add] 8.3.4 [dcl.array] 了解数组的详细信息。 —尾注],除了在数组操作数的情况下,如果该操作数的结果是左值 是左值,否则是 xvalue。

Y{} 的结果是来自5.2.3 部分的prvalue [expr.type.conv]

类似地,一个简单类型说明符或类型名称说明符后跟一个 braced-init-list 创建指定类型的临时对象 使用指定的花括号初始化列表进行直接列表初始化(8.5.4), 它的值为 将临时对象作为prvalue。

所以(Y { })[0] 的结果应该是一个 xvalue,因此格式错误,因为赋值需要在左操作数上修改左值:

赋值运算符 (=) 和复合赋值运算符都从右到左分组。都需要一个 可修改的左值作为它们的左操作数 [...]

我们可以从draft C++14 standard 中的缺陷报告中找到更新的措辞,因此此更改是在 C++11 之后应用的,但可能适用于 C++11,因为这是通过缺陷报告应用的。

为什么5.3.1[expr.unary.op] 部分没有更新我不清楚,说结果是一个 lvalue 似乎有些不一致并非在所有情况下都如此。

更新

提交了clang bug report

【讨论】:

  • 你能解释为什么 xvalues 在这种情况下是不可修改的吗?
【解决方案2】:

C++ 标准草案 (N4527) § 5.2.1 第 1 条说:

表达式 E1[E2] 与 *((E1)+(E2)) 相同(根据定义)

§ 5.3.1 第 1 条说:

一元*运算符执行间接:应用它的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是一个左值引用该对象或表达式指向的函数。

【讨论】:

  • 似乎是语言不一致。
猜你喜欢
  • 2013-01-27
  • 1970-01-01
  • 1970-01-01
  • 2017-03-04
  • 2013-10-04
  • 1970-01-01
  • 2021-09-16
  • 2010-11-23
相关资源
最近更新 更多