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,每个都是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
—结束示例]—结束注释]
3 变量x,其名称显示为潜在评估表达式ex,由ex使用,除非将左值到右值转换 (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::a 和S::b 是左值,所以条件表达式x ? S::a : S::b 也是一个左值。这意味着左值到右值的转换不会立即应用于S::a 和S::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:
- 如果
e 是E1[E2] 形式的glvalue 下标表达式(5.2.1),则该集合包含E1 的潜在结果。