【问题标题】:When is a variable odr-used in C++14?何时在 C++14 中使用变量 odr?
【发布时间】:2014-06-22 21:28:11
【问题描述】:

C++14 草案 (N3936) 在 §3.2/3 中声明:

名称显示为潜在求值表达式 ex 的变量 x 被 odr-used 使用,除非将左值到右值转换 (4.1) 应用于 x 产生不调用任何非平凡函数的常量表达式 (5.19)并且,如果 x 是一个对象,则 ex 是表达式 e 的潜在结果集合中的一个元素,其中左值到右值的转换 (4.1) 应用于 e,或者 e 是丢弃值表达式(条款5).

这对我没有任何意义:如果表达式e丢弃值表达式,则取决于使用e 的上下文。 expression-statement(第 6.2 节)中使用的每个表达式都是 discarded-value 表达式左值到右值的转换是否应用于e也取决于e使用的上下文。

此外,一个表达式出现在另一个表达式的潜在结果集合中意味着什么。人们需要一个表达式相等的概念才能确定一个集合的成员。但是我们没有参照透明度,所以我看不出这是如何实现的。

为什么从 C++11 改为 C++14?这应该如何解释?就目前而言,这没有任何意义。

【问题讨论】:

    标签: c++ c++14 one-definition-rule


    【解决方案1】:

    odr使用的目的

    非正式地,odr-use 变量的含义如下:

    如果程序中任何地方的任何表达式直接获取对象的地址或将引用绑定到对象,则必须定义该对象。

    最新草案中的说明

    在最新版本的规范 §3.2 中已经阐明(见Draft C++14 on GitHub):

    2 表达式可能会被计算,除非它是未计算的操作数(第 5 条)或其子表达式。表达式e 的潜在结果集定义如下:

    • 如果 e 是 id 表达式 (5.1.1),则该集合仅包含 e
    • 如果e 是类成员访问表达式 (5.2.5),则该集合包含对象表达式的潜在结果。
    • 如果e 是指向成员的表达式 (5.5),其第二个操作数是常量表达式,则该集合包含对象表达式的潜在结果。
    • 如果e 的形式为 (e1),则该集合包含 e1 的潜在结果。
    • 如果e 是一个泛左值条件表达式 (5.16),则该集合是第二个和第三个操作数的潜在结果集合的并集。
    • 如果e 是逗号表达式 (5.18),则该集合包含右操作数的潜在结果。
    • 否则,集合为空。

    [注意:这个集合是一组(可能是空的)id-expressions,每个都是ee的子表达式。

    [ 示例:在以下示例中,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
    

    —结束示例]—结束注释]

    3 变量x,其名称显示为潜在评估表达式exex使用,除非将左值到右值转换 (4.1) 应用于@ 987654342@ 产生一个常量表达式 (5.19),它不调用任何非平凡函数,如果 x 是一个对象,ex 是表达式 e 的一组潜在结果中的一个元素,其中左值-右值转换 (4.1) 应用于 e,或者 e 是丢弃值表达式(第 5 条)。

    C++11 的情况如何?

    C++11 中的§3.2/2 内容如下:

    一个表达式可能被计算,除非它是一个未计算的操作数(第 5 条)或其子表达式。名称显示为潜在求值表达式的变量是 odr-used 除非它是一个满足出现在常量表达式 (5.19) 中的要求并且左值到右值转换 (4.1) 是立即 已应用。

    这些措辞的问题是DR 712。考虑这个例子:

    struct S {
      static const int a = 1;
      static const int b = 2;
    };
    int f(bool x) {
      return x ? S::a : S::b;
    }
    

    由于S::aS::b 是左值,所以条件表达式x ? S::a : S::b 也是一个左值。这意味着左值到右值的转换不会立即应用于S::aS::b,而是应用于条件表达式的结果。这意味着按照 C++11 的措辞,这些静态数据成员是 odr-used 并且需要定义。但实际上只使用了值,因此不需要定义静态数据成员 - 声明就足够了。 C++14 草案的新措辞解决了这个问题。

    新措辞是否解决了所有问题?

    没有。在以下示例中,变量 S::a 仍然是 odr-used:

    struct S { static constexpr int a[2] = {0, 1}; };
    void f() {
        auto x = S::a[0];
    }
    

    因此我提交了一个新的issue 以将以下项目符号添加到 §3.2/2:

    • 如果eE1[E2] 形式的glvalue 下标表达式(5.2.1),则该集合包含E1 的潜在结果。

    【讨论】:

    • 很好,但我们需要双方的潜在结果(E2E1)来允许反向下标(0[S::a])。
    • 另外,如果允许下标,那么间接也应该是 (*S::a)。这里与 5p11 有一个有趣的相似之处。
    • @ecatmur 我同意反向订阅。考虑间接,我不太确定提交的问题是否可以实现。由于订阅与间接相同,我们总是有数组到指针的标准转换。这意味着我们必须能够以某种方式获取变量的地址,这意味着 odr-use。
    • 如果积分边是一个常量表达式,其值尊重另一边的数组边界,我们可以回避数组到指针的转换;我认为这应该在 3.2p2 项目符号中。剩下的就是左值到右值的转换,我们只需要调整 3.2p3 以将其应用于数组的元素类型而不是数组本身。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-14
    • 2017-05-06
    • 1970-01-01
    • 2013-10-19
    • 2013-10-07
    相关资源
    最近更新 更多