繁体   English   中英

是否使用了静态 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 示例

undefined reference to `Foo::FOO1'

这表明它是 ODR 使用的。 是哪个?


我知道上面的代码使用 -O 构建得很好,但在 真实(更复杂)的情况下

  • 代码编译并链接到 -O2
  • 代码得到上面undefined reference错误 LinkTimeOptimization ( -O2 -flto )

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

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

名称显示为潜在求值表达式ex的变量xex odr 使用除非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

这里的重要部分是我们没有直接构造一个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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM