规则是[基本.def.odr]/4:
一个变量x
其名称显示为可能评估的表达式ex
is odr-used by ex
unless将左值到右值转换应用于x
产生一个不调用任何非平凡函数的常量表达式,并且,如果x
是一个对象,ex
是表达式的潜在结果集合中的一个元素e
,其中左值到右值转换 ([conv.lval]) 应用于e
, or e
是一个废弃值表达式 ([expr.prop])。
第一部分显然是满意的(FOO1
is constexpr
因此,左值到右值的转换确实会产生一个常量表达式,而无需调用非平凡函数),但这是第二个吗?
我们正在构建一个map
。相关的那里的构造函数需要一个initializer_list<value_type>
,也就是说initializer_list<pair<const string, int>>
. pair
has a 一堆构造函数,但这里要调用的是:
template <class U1, class U2>
constexpr pair(U1&& x, U2&& y); // with U1 = char const*&, U2 = int
这里重要的部分是我们不是直接构建一个string
,我们正在经历这个转换构造函数pair
,其中涉及绑定对的引用FOO1
。这是一种 ODR 使用。这里没有左值到右值的转换,也不是丢弃值表达式。
基本上,当你获取某个东西的地址时,这就是 odr-use - 它必须有一个定义。所以你必须添加一个定义:
constexpr char const* Foo::FOO1;
另一方面,请注意:
std::string s = FOO1;
would not成为网上解决用途。这里我们直接调用一个构造函数char const*
参数,这将是左值到右值的转换。
在 C++17 中,我们得到了这个新句子[dcl.constexpr]:
使用 constexpr 说明符声明的函数或静态数据成员隐式是内联函数或变量 ([dcl.inline])。
这不会改变 odr-use 的任何内容,FOO1
仍然在您的程序中使用 odr。但它确实使FOO1
隐式地是一个内联变量,因此您不必显式地为其添加定义。很酷。
另请注意,程序编译和链接并不意味着缺少定义的变量未被 odr 使用。
那么这表明优化 (-O) 和 LinkTimeOptimization (-flto) 都会影响 ODR 使用规则吗?
他们不。优化就是这么酷。