是否使用静态 constexpr 变量 odr?

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 example:

undefined reference to `Foo::FOO1'

表示它是ODR-used。是哪个?


我知道上面的代码是用 -O 构建的,但是在 a real (and more complex) case:

所以这表明优化 (-O) 和 LinkTimeOptimization (-flto) 都会影响 ODR 使用规则?这在 C++14 和 C++17 之间有变化吗?

规则是[basic.def.odr]/4:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion to x yields a constant expression that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion ([conv.lval]) is applied to e, or e is a discarded-value expression ([expr.prop]).

第一部分显然是令人满意的(FOO1constexpr 所以左值到右值的转换确实产生了一个常量表达式而不调用非平凡函数),但第二部分是吗?

我们正在构建 map。相关的 constructor there takes an initializer_list<value_type>, which is to say an initializer_list<pair<const string, int>>. pair has a bunch of constructors,但此处将调用的是:

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;

不会 是 ODR 用途。这里我们直接调用一个带有 char const* 参数的构造函数,这将是一个左值到右值的转换。


在 C++17 中,我们在 [dcl.constexpr] 中得到了这个新句子:

A function or static data member declared with the constexpr specifier is implicitly an inline function or variable ([dcl.inline]).

这不会改变关于 odr-use 的任何内容,FOO1 仍然在您的程序中使用 odr。但它确实使 FOO1 隐式成为一个内联变量,因此您不必为它显式添加定义。很酷。


另请注意,仅因为程序编译和链接并不意味着缺少定义的变量未被 ODR 使用。

So it indicates that both optimizations (-O) and LinkTimeOptimization (-flto) would affect ODR-use rule?

他们没有。这样的优化很酷。