c++ 14草案(N3936)在§3.2/3中规定:
变量x的名字出现为潜在求值表达式ex,除非将左值到右值转换(4.1)应用于x产生一个不调用任何重要函数的常量表达式(5.19),并且如果x是对象,则ex是表达式e的潜在结果集合的一个元素,其中左值到右值转换(4.1)应用于e,或者e是一个丢弃值表达式(第5条)。
这对我来说没有任何意义:如果表达式e
是丢弃值表达式取决于使用e
的上下文。表达式语句(第6.2节)中使用的每个表达式都是丢弃值表达式。如果左值到右值的转换适用于e
,也取决于e
使用的上下文。
此外,一个表达式在另一个表达式的潜在结果集合中是什么意思?我们需要一个表达式相等的概念来确定集合的成员。但是我们没有参考透明度,所以我不知道这是如何实现的。
为什么从c++ 11更改到c++ 14?这应该怎么解释呢?就目前情况来看,这没有意义。
odr-use的目的
非正式地,使用变量的意味着以下内容:
如果程序中任何地方的表达式接受一个对象的地址或直接绑定一个对象的引用,则必须定义这个对象。
最新草案中的澄清
最新版本的规范§3.2已被澄清(参见GitHub上的c++ 14草案):
2表达式可能被求值,除非它是未求值的操作数(第5条)或其子表达式。表达式
e
的可能结果集定义如下:
- 如果
e
是id表达式(5.1.1),则该集合只包含e
。- 如果
e
是类成员访问表达式(5.2.5),则该集合包含该对象表达式的可能结果。- 如果
e
是第二个操作数为常量的成员指针表达式(5.5),则该集合包含该对象表达式的可能结果。- 如果
e
的形式为(e1),则该集合包含e1的可能结果。- 如果
e
是glvalue条件表达式(5.16),则该集合是第二个和第三个操作数的可能结果集合的并集。- 如果
e
是逗号表达式(5.18),则该集合包含右操作数的可能结果。[注意:该集合是一个id-表达式的集合(可能为空),每个id-表达式要么是
e
,要么是e
的子表达式。[示例:在下面的示例中,
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
-end note]
3变量
x
的名字出现为潜在求值表达式ex
,则ex
使用,除非将左值到右值转换(4.1)应用于x
产生一个不调用任何重要函数的常量表达式(5.19),并且如果x
是一个对象,则ex
是表达式e
的潜在结果集合中的一个元素,其中左值到右值转换(4.1)应用于e
;或e
是一个丢弃值表达式(第5条)。
c++ 11的情况如何?
c++ 11中的§3.2/2为:
表达式可能被求值,除非它是未求值的操作数(第5条)或其子表达式。如果变量名出现在潜在求值表达式中,则不使用该变量,除非该变量是满足出现在常量表达式(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::a
和S::b
是左值,所以条件表达式x ? S::a : S::b
也是左值。这意味着左值到右值的转换是,不是立即应用于S::a
和S::b
,而是应用于条件表达式的结果。这意味着,根据c++ 11的措辞,这些静态数据成员是禁止使用的,并且需要定义。但实际上只使用值,因此没有必要定义静态数据成员—声明就足够了。c++ 14草案的新措辞解决了这个问题。
新的措辞是否解决了所有问题?
。在下面的示例中,变量S::a
仍然是odr使用的:
struct S { static constexpr int a[2] = {0, 1}; };
void f() {
auto x = S::a[0];
}
因此,我提交了一个新问题,将以下项目添加到§3.2/2:
- 如果
e
是形式为E1[E2]
的glvalue下标表达式(5.2.1),则该集合包含E1
的潜在结果。