C++ gcc和clang中constexpr静态成员变量的链接器错误

C++ gcc和clang中constexpr静态成员变量的链接器错误,c++,c++11,c++14,C++,C++11,C++14,我有一个片段: enum class EC {a, b}; struct B { constexpr B(EC ec): ec_(ec) {} EC ec_; }; struct A_base { constexpr A_base(B b): b_(b) { } B b_; }; struct A: A_base { static constexpr B bbb = EC::a; constexpr A(B bbbb): A_base(bbb

我有一个片段:

enum class EC {a, b};

struct B {
    constexpr B(EC ec): ec_(ec) {}
    EC ec_;
};

struct A_base {
    constexpr A_base(B b): b_(b) { }
    B b_;
};

struct A: A_base {
    static constexpr B bbb = EC::a;
    constexpr A(B bbbb): A_base(bbbb) { }
};

int main()
{
    A a1(A::bbb);    // 1
    A a2{A::bbb};    // 2

    A a3 = A::bbb;   // 3
    A a4 = {A::bbb}; // 4
}
它由支持c++17的现代编译器编译而成。使用c++11和c++14标准支持链接器时发生错误。这个问题已经在和其他一些讨论中讨论过了。我理解为什么会发生这种错误

但有些事情我不明白:

  • 使用clang时,我总是会遇到链接器错误。但是,当我设置-O3优化或将a1、a2、a3、a4变量声明为constexpr时,它会内联A::bbb,并且没有错误
  • 没有优化的Gcc只需要参考a3和a4中的A::bbb。通过优化和constexpr,它的行为完全像叮当声
  • 编译器与关闭的优化不同是正常的吗?为什么gcc在C++17之前不将a1、a2初始化等同于a3、a4?

    A
    s的所有初始化都调用
    A::A(B)
    构造函数,它需要复制A
    B
    ,该构造函数使用编译器生成的
    B::B(B const&)
    。将变量绑定到引用(
    a::bbb
    )是该变量的odr用法。[basic.def.odr]中的具体规则是:

    除非应用 从左值到右值的转换(4.1)到x生成一个不调用任何非平凡函数的常量表达式(5.20) 函数,如果x是对象,则ex是表达式e的潜在结果集的元素,其中 左值到右值的转换(4.1)应用于e,或者e是一个废弃的值表达式(第5条)

    对复制构造函数的第一个调用将涉及将
    A::bbb
    绑定到引用,该引用既不是左值到右值的转换,也不是丢弃的值表达式,因此使用odr

    最重要的规则是:

    每个程序应包含每个非内联函数或变量的一个定义,该函数或变量是在该程序中使用的odr,而不是丢弃的语句(6.4.1);无需诊断

    A::bbb
    使用odr,但缺少定义,因此我们违反了该规则-通常称为odr违反。但由于编译器在这种情况下不需要发出诊断(“无需诊断”,简称NDR),因此程序是未定义的行为。对于程序员来说,这类问题有时是令人沮丧的,但对于编译器来说,诊断这些问题是非常困难的,所以这是我们不得不面对的问题

    很可能在更高的优化级别上,编译器只是省略了副本,因此不需要为
    a::bbb
    调用
    B::B(B const&)
    。。。至于为什么不同的初始化处理方式不同?可能是由于不同的优化过程。最终,它不会改变这是一个odr冲突的事实——不管代码是否编译和链接


    在C++17之后
    因此,
    static constexpr
    数据成员是隐式内联的,这意味着现在
    a::bbb
    确实有了定义,并且现在没有odr冲突。C++17很酷

    是的,这很正常。这是未定义的行为。编译器可以做任何事情。“编译器与关闭的优化不同是正常的吗”--是的。如果在更高的优化中,编译器/链接器能够消除对某个符号的所有引用,则可以不定义该符号。(尽管如此,为了确保程序的格式良好,您还是应该定义它。)这真的是一种未定义的行为吗?请您提供一个链接,将这种情况描述为未定义的行为,好吗?@SeleznevAnton请参见C++14的[basic.def.odr]“每个程序都应包含该程序中odr使用的每个非内联函数或变量的一个定义;无需诊断”。上一段定义了使用的odr,其中包括代码中的用法。“无需诊断”是指未定义的行为([intro.compliance]/2.3)@Barry同意,但我赞成Sam的解释,反对你的非建设性行为!