[英]Is static constexpr variable odr-used?
给出以下代码,是否使用Foo::FOO1
ODR?
#include <iostream>
#include <map>
#include <string>
class Foo
{
public:
static constexpr auto FOO1 = "foo1";
void bar();
};
void Foo::bar()
{
const std::map<std::string, int> m = {
{FOO1, 1},
};
for (auto i : m)
{
std::cout << i.first << " " << i.second << std::endl;
}
}
int main()
{
Foo f;
f.bar();
return 0;
}
用-O1
或更高版本编译代码,没问题,但如果用-O0
编译,我会得到以下错误(请参阅coliru 示例:
undefined reference to `Foo::FOO1'
这表明它是 ODR 使用的。 是哪个?
我知道上面的代码使用 -O 构建得很好,但在 真实(更复杂)的情况下:
undefined reference
错误 LinkTimeOptimization ( -O2 -flto
) 所以它表明优化( -O
)和 LinkTimeOptimization ( -flto
)都会影响 ODR 使用规则? 这在 C++14 和 C++17 之间有变化吗?
规则是[basic.def.odr]/4 :
名称显示为潜在求值表达式
ex
的变量x
被ex
odr 使用,除非对x
应用左值到右值转换产生不调用任何非平凡函数的常量表达式,并且如果x
是对象,ex
是表达式e
的潜在结果集的一个元素,其中左值到右值转换 ([conv.lval]) 应用于e
,或者e
是丢弃值表达式 ([expr.prop ])。
第一部分显然是满意的( FOO1
是constexpr
所以左值到右值的转换确实会产生一个常量表达式,而不会调用非平凡的函数),但第二部分是吗?
我们正在构建map
。 那里的相关构造函数采用一个initializer_list<value_type>
,也就是说一个initializer_list<pair<const string, int>>
。 pair
有一堆构造函数,但这里会调用的构造函数是:
template <class U1, class U2>
constexpr pair(U1&& x, U2&& y); // with U1 = char const*&, U2 = int
这里的重要部分是我们没有直接构造一个string
,我们正在通过pair
这个转换构造函数,它涉及绑定一个对FOO1
的引用。 这是一个 odr 使用。 这里没有左值到右值的转换,这也不是丢弃值表达式。
基本上,当你获取某物的地址时,这是一个 odr 使用——它必须有一个定义。 所以你必须添加一个定义:
constexpr char const* Foo::FOO1;
请注意,另一方面,这是:
std::string s = FOO1;
不会是 odr 使用。 这里我们直接调用一个带有char const*
参数的构造char const*
,这将是一个左值到右值的转换。
在 C++17 中,我们在[dcl.constexpr] 中得到了这个新句子:
使用 constexpr 说明符声明的函数或静态数据成员隐式是内联函数或变量 ([dcl.inline])。
这不会改变 odr 使用的任何内容, FOO1
仍然在您的程序中使用 odr。 但它确实使FOO1
隐式地成为内联变量,因此您不必显式地为其添加定义。 很酷。
还要注意,仅仅因为程序编译和链接并不意味着缺少定义的变量没有被 odr 使用。
所以它表明优化(-O)和LinkTimeOptimization(-flto)都会影响ODR使用规则?
他们不。 优化就是这么酷。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.