是静态constexpr变量odr使用?

Min*_*ine 3 c++ one-definition-rule c++14

给出以下代码,是否使用Foo::FOO1ODR?

#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;
}
Run Code Online (Sandbox Code Playgroud)

使用-O1或以上编译代码,没关系,但如果编译-O0,我得到以下错误(请参阅coliru示例:

undefined reference to `Foo::FOO1'
Run Code Online (Sandbox Code Playgroud)

表示它是ODR使用的.这是什么?


我知道上面的代码是用-O构建的,但是在一个真实的(更复杂的)情况下:

  • 代码编译并与-O2良好链接
  • 代码获取上述undefined reference错误LinkTimeOptimization(-O2 -flto)

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

Bar*_*rry 6

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

一种可变x为潜在评估表达式出现的名字exODR使用的ex 除非施加左值到右值转换到x产率的常量表达式不调用任何非平凡函数,并且如果x是一个对象,ex是的一个元素表达式的潜在结果集e,其中应用了左值到右值的转换([conv.lval])e,或者e是丢弃值表达式([expr.prop]).

第一部分是明显满足(FOO1constexpr所以左值到右值转换确实产生恒定表达,而不必调用非平凡函数),但是在第二?

我们正在构建一个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
Run Code Online (Sandbox Code Playgroud)

这里的重要部分是我们不是直接构造一个string,我们正在经历这个转换构造函数pair,它涉及绑定一个引用FOO1.这是一种奇怪的用法.这里没有左值到右值的转换,也不是丢弃值表达式.

基本上,当你拿一些东西的地址,这是一种使用 - 它必须有一个定义.所以你必须添加一个定义:

constexpr char const* Foo::FOO1;
Run Code Online (Sandbox Code Playgroud)

另请注意,这个:

std::string s = FOO1;
Run Code Online (Sandbox Code Playgroud)

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


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

使用constexpr说明符声明的函数或静态数据成员隐式地是内联函数或变量([dcl.inline]).

这并没有改变任何关于odr-use的东西,FOO1在你的程序中仍然使用了odr.但它确实FOO1隐式地生成内联变量,因此您不必为其明确添加定义.很酷.


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

所以它表明优化(-O)和LinkTimeOptimization(-flto)都会影响ODR使用规则?

他们不.优化很酷.