【问题标题】:Trying to understand [basic.def.odr]/2 in C++14 (N4140)试图理解 C++14 中的 [basic.def.odr]/2 (N4140)
【发布时间】:2015-05-02 00:51:55
【问题描述】:

[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

【问题讨论】:

  • 没有 C++14 标准的人无法回答这个问题。您可以通过显示您所询问的代码来提供帮助。那么它可以由没有访问 C++14 标准但​​了解 C++14 标准的人来回答。
  • @JonathanLeffler 对于使用(仅)C++14 标准的人来说这是无法回答的。此示例仅出现在较新的草稿中(可公开获得)。但我同意这个例子应该是问题的一部分。
  • @dyp:所以tag和title不对(应该是C++17),问题应该指向最近的草稿,大家需要参考一下。基本上,当您提出问题时,您需要帮助人们帮助您!它也从根本上改变了这个问题。如果是草稿,规范中仍然可能存在错误。如果它在标准中,则不太可能(尽管并非不可能)。
  • 这个出现在n4140,也就是C++14。顺便说一句,“我们如何推断 n 的初始化程序包含”是不正确的——这与初始化程序包含的内容无关,而与它返回的内容有关(可能)
  • 谢谢,@MattMcNabb:我知道在哪里可以找到这些文件。 AFAIK,目前还没有价格合理的官方(ISO 或 ANSI 或 ...)C++14 PDF 可用(但我本月尚未检查)。使用委员会草案而不是最终标准并不完全安全。这个问题应该对于后续阅读它的人来说是可以理解的——他们不应该去寻找标准来使用答案。很抱歉,这里的问题应该包括示例代码,但不一定不止于此。

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


【解决方案1】:

我正在使用基于 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的odr使用,[basic.def.odr]p3

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

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

  1. 要么 ex 未潜在评估,要么
  2. 必须满足以下所有条件:
    1. “将左值到右值的转换应用到x 会产生一个不会调用任何重要函数的常量表达式”
    2. ex 是表达式e”的潜在结果集合中的一个元素 并且满足以下任一条件
      1. "任一左值到右值的转换应用于e"
      2. e 是丢弃值表达式”

请注意,第 2 点的意思是“是任何表达式 e [其中 e 满足某些要求] 的潜在结果集合中的一个元素”,而不是“所有表达式 e 它是其中的一部分”。进一步的讨论可以在std-discussion mailing list找到。

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

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

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

该例外不适用,S::x 在第二次出现时被 odr-used。

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

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

  1. False(因为所有这些表达式都可能被评估),
  2. 必须满足以下所有条件:
    1. True(因为将 l-t-r 转换应用到 S::x 会产生一个不调用 任何 函数的常量表达式)
    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-used。

您可以在问答Does initialization entail lvalue-to-rvalue conversion? Is int x = x; UB? 中找到很多关于初始化中左值到右值转换的信息。这里的情况可能更容易一些,因为 rhs 的类型为 int const。这可能需要一个限定转换,它需要一个纯右值操作数(这可能会隐式调用左值到右值的转换)。

【讨论】:

  • 但这与S::x 不是在条件运算符的第二个操作数中使用 odr,而是在其第三个操作数中使用 odr 的事实有什么关系?
  • @LeonTrotski 我同意了。基本上,第一次出现不是 odr-used,因为它是“立即”被读取的,而第二次出现必须绑定到引用。
  • 我可以理解,但我在建立您的第一个答案和第二个答案之间的关系时遇到了一些困难。
  • @LeonTrotski 这是可以理解的。我想通过将左值到右值的转换应用于整个表达式b ? (1, S::x) : f(S::x) 来建立联系,这将建立一个链接。但是,我不确定这种转换是否真的应用于初始化程序。
  • 我正在努力理解 [basic.def.odr]/3。在 C++11 中,这一段是这样写的:“名称显示为潜在求值表达式的变量是 odr-used 除非它是一个满足出现在常量表达式 (5.19) 和左值中的要求的对象- 立即应用到右值转换。"在 C++11 中,not 被 odr 使用与 立即 应用的左值到右值转换之间的关系是什么?也就是说,这一段中立即这个词的相关性是什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-25
  • 1970-01-01
  • 1970-01-01
  • 2016-06-11
  • 2016-10-15
  • 2013-10-08
相关资源
最近更新 更多