odr使用的目的
非正式地,变量的odr 使用意味着以下内容:
如果程序中任何地方的任何表达式直接获取对象的地址或将引用绑定到对象,则必须定义该对象。
最新草案中的澄清
在最新版本的规范中,第 3.2 条已得到澄清(参见GitHub 上的 C++14 草案):
2 一个表达式可能会被计算,除非它是一个未计算的操作数(第 5 条)或其子表达式。表达式的潜在结果集e
定义如下:
- 如果
e
是 id 表达式 (5.1.1),则该集合仅包含e
.
- 如果
e
是类成员访问表达式(5.2.5),则该集合包含对象表达式的潜在结果。
- If
e
是一个指向成员的表达式 (5.5),其第二个操作数是一个常量表达式,该集合包含对象表达式的潜在结果。
- 如果
e
具有 (e1) 形式,则该集合包含 e1 的潜在结果。
- If
e
是一个泛左值条件表达式 (5.16),该集合是第二个和第三个操作数的潜在结果集合的并集。
- If
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
—结束示例] —结束说明]
3x
名称显示为潜在求值表达式的变量ex
被 odr 使用,ex
除非应用左值到右值转换 (4.1) 以x
生成不调用任何非平凡函数的常量表达式 (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
and 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];
}
因此,我提交了一个新问题,将以下项目符号添加到 §3.2/2:
- 如果
e
是形式的泛左值下标表达式(5.2.1)E1[E2]
,则集合包含 的潜在结果E1
。