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

Leo*_*eon 2 c++ language-lawyer c++14

[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
Run Code Online (Sandbox Code Playgroud)

dyp*_*dyp 12

我正在使用最近基于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.现在,p2.1适用并告诉我们第一次出现是初始化器的潜在结果集的一部分.

对于第二个子表达式f(S::x),仅适用p2.7.它的潜在结果集是空的,因此它不会对初始化程序的潜在结果集添加任何内容.


至于S::x[basic.def.odr] p3的使用方法

一种可变x为潜在评估表达式出现的名字 ex被ODR-使用ex,除非将所述左值到右值转换到x产率的常量表达式不调用任何非平凡函数,并且如果x是一个对象,ex是的一个元素表达式的潜在结果集e,其中应用了左值到右值的转换e,或者e是丢弃值表达式.

让我们将其拆分为以下步骤:x表达式中变量的出现ex构成了odr-use,除非:

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

请注意,第2点意味着"是任何表达式e[ e满足某些要求] 的潜在结果集合的元素",而不是" e它所属的所有表达式".可以在std讨论邮件列表中找到进一步的讨论.

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

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

  1. 假(因为所有这些表达式都可能被评估),或者
  2. 必须满足以下所有条件:
    1. True(因为应用ltr转换S::x产生一个不调用任何函数的常量表达式)和
    2. 第二次出现的唯一表达式S::x是潜在结果集合的元素S::x本身.它不是潜在结果的一部分f(S::x).必须满足以下任一条件:
      1. 要么为假(因为绑定S::x到函数参数时不应用左值到右值的转换f)
      2. 或者假(因为S::x不是丢弃值表达式)

例外不适用,S::x通过第二次出现使用odr.

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

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

  1. 假(因为所有这些表达式都可能被评估),或者
  2. 必须满足以下所有条件:
    1. True(因为应用ltr转换S::x产生一个不调用任何函数的常量表达式)和
    2. 第一次出现S::x是初始化程序中所有表达式的潜在结果集的元素.必须满足以下任一条件:
      1. 真 -左值到右值转换肯定是不适用的表情S::x,1, S::x,(1, S::x).可以认为它适用于b ? (1, S::x) : f(S::x)(见下文)
      2. 或者假(这些表达式都不是丢弃值表达式)

尚不清楚初始化是否应用左值到右值的转换.可以认为必须读取"左值表达式的值"才能int从类型的表达式初始化int const.如果我们遵循这个假设,则应用左值到右值的转换b ? (1, S::x) : f(S::X).第一次出现S::x是该表达式的潜在结果集的一个元素(参见本答案的第一部分).因此,上述点句3.0适用,并且S::x是不 ODR-用于通过第一次出现.

您可以在Q&A初始化中找到有关左值到右值转换的大量信息.初始化是否需要左值到右值的转换?是int x = x;UB吗?.由于rhs具有类型,因此这种情况可能更容易一些int const.这可能需要一个限定转换,它需要一个prvalue操作数(这可能会隐式调用左值到右值的转换).