试图理解C++14中的[basic.def.odr]/2(N4140)



〔basic.def.odr〕/2中的示例以以下句子开头:

在以下示例中n的初始值设定项包含第一个S::x子表达式,但不包含第二个S::x子表达式。

根据这段中的定义,我们如何推断n的初始值设定项包含第一个S::x子表达式,但不包含第二个S::x子表达式?

编辑请参阅以下示例的剩余部分:

struct S { static const int x = 0; };
const int &f(const int &r);
int n = b ? (1, S::x) // S::x is not odr-used here
: f(S::x); // S::x is odr-used here, so
// a definition is required

我使用的是最近基于N4296的github草案。实际的C++14国际标准不包含此示例,也不包含项目符号的编号。此处的相关规范实际上是相同的。

我们分解初始化器中的表达式:b ? (1, S::x) : f(S::x)

表达式(1, S::x)是类型为int const的左值。表达式f(S::x)是后缀表达式,是类型为int const的左值。

因此表达式b ? (1, S::x) : f(S::x)是类型为int const的左值。因此,它满足[basic.def.odr]p2.5,并且潜在结果集是子表达式(1, S::x)f(S::x)的潜在结果集的并集。

对于第一个子表达式(1, S::x),我们通过p2.4去掉括号。结果1, S::x是逗号表达式。我们应用p2.6得到S::x。现在,第2.1页适用,告诉我们,第一次出现是初始化器的潜在结果集的一部分。

对于第二个子表达式f(S::x),只有p2.7适用。它的潜在结果集是空的,因此它不会向初始值设定项的潜在结果集中添加任何内容。


关于S::x的odr使用,[basic.def.odr]p3

一个变量x,其名称显示为潜在的求值表达式exex使用,除非应用左值到右值的转换到x生成一个不调用的常量表达式任何非平凡函数,如果x是对象,则ex是表达式e的一组潜在结果,其中左值到右值的转换应用于e,或者e是丢弃的值表达式。

让我们将其分为几个步骤:变量x在表达式ex中的出现构成odr使用,除非:

  1. 要么ex没有潜在评估,要么
  2. 必须满足以下所有条件:
    1. "将左值到右值转换应用于x会生成一个常量表达式,该表达式不会调用任何非平凡函数">
    2. "ex是表达式e"> 的一组潜在结果的元素,并且以下任一项成立
      1. ">左值到右值的转换应用于e">
      2. ">e是一个已丢弃的值表达式">

注意,点2的意思是"是ANY表达式e的潜在结果集的一个元素[其中e满足某些要求]",而不是"它所属的所有表达式e"。进一步的讨论可以在std讨论邮件列表中找到。

将步骤应用于第二次出现的`S::x`

它是表达式S::xf(S::x)b ? (1, S::x) : f(S::x)的一部分。

  1. 错误(因为所有这些表达式都可能被求值),
  2. 必须满足以下所有条件:
    1. True(因为对S::x应用l-t-r转换会产生一个常量表达式,该表达式不会调用任何函数)
    2. S::x的第二次出现是潜在结果集的元素的唯一表达式是S::x本身。这不是f(S::x)潜在结果的一部分以下任一项必须保持
      1. 要么为false(因为在将S::x绑定到f的函数参数时不应用左值到右值的转换)
      2. 或false(因为S::x不是丢弃的值表达式)

异常不适用,S::x通过第二次出现而被odr使用。

将步骤应用于首次出现的`S::x`

它是表达式S::x1, S::x(1, S::x)b ? (1, S::x) : f(S::x)的一部分。

  1. 错误(因为所有这些表达式都可能被求值),
  2. 必须满足以下所有条件:
    1. True(因为对S::x应用l-t-r转换会产生一个常量表达式,该表达式不会调用任何函数)
    2. S::x的第一次出现是初始化器中它所属的所有表达式的潜在结果集的一个元素以下任一项必须保持
      1. true-左值到右值的转换肯定不适用于表达式S::x1, S::x(1, S::x)。可以说,它适用于b ? (1, S::x) : f(S::x)(见下文)
      2. 或false(这些表达式都不是丢弃的值表达式)

初始化是否将左值应用于右值转换尚不清楚。可以争辩说,必须读取"左值表达式的值",才能从类型为int const的表达式初始化int。如果我们遵循这个假设,那么左值到右值的转换将应用于b ? (1, S::x) : f(S::X)S::x的第一次出现是该表达式的一组潜在结果中的一个元素(见本答案的第一部分)。因此,上面的项目符号点3.0适用,并且S::x而不是第一次出现时使用的odr。

你可以在Q&A初始化是否需要左值到右值的转换?int x = x;是UB吗?。由于rhs的类型为int const,因此这里的情况可能会简单一些。这可能需要一个限定转换,它需要一个prvalue操作数(这可能会隐式调用左值到右值的转换)。

最新更新