用作默认参数的未优化 constexpr 的未定义引用

Undefined reference for unoptimised constexpr used as default parameter

我不明白为什么下面的代码在 GCC 优化的情况下编译,但在未优化时无法 link 和 "undefined reference to `base::A_VAL'"。我在做一些狡猾的事吗?这是编译器错误(从来不是)吗?这是 Ubuntu.

上的 g++ 5.4.0

base.h:

class base {
public:
    static constexpr unsigned int A_VAL{0x69U};
};

derived.h:

#include "base.h"
#include <iostream>

using namespace std;

class derived : public base
{
public:
    int some_func(void) {
        cout << "Some func" << endl;
        return 0;
    }
};

concrete.h:

#include "derived.h"

#include <utility>

class concrete : public derived
{
public:
    concrete(int a, std::pair<unsigned int, unsigned int> data = {A_VAL, A_VAL}) {
        some_func();
        std::cout << "First: " << data.first << " Second: " << data.second << endl;
    }
};

test.cpp:

#include "concrete.h"

int main (int argc, char *argv[])
{
    concrete c{1};

    c.some_func();
}

g++ -O2 -std=c++14 -o test test.cpp

很好。

g++ -O0 -std=c++14 -o test test.cpp

/tmp/ccm9NjMC.o: In function `main':
test.cpp:(.text+0x23): undefined reference to `base::A_VAL'
test.cpp:(.text+0x28): undefined reference to `base::A_VAL'
collect2: error: ld returned 1 exit status

在优化 GCC 时可能能够确定(在内联常量折叠之后)concrete 的构造函数的主体几乎可以被

替换
some_func();
std::cout << "First: " << A_VAL << " Second: " << A_VAL << endl;

Since operator<< for the standard stream class 按值取整数,而 A_VAL 是常量表达式,上面的调用不需要 A_VAL 的任何存储。它的值只是插入。因此,GCC 不需要 A_VAL 的 class 定义,而静态 class 成员通常需要这样。

当不进行优化时,GCC 很可能会初始化 pair 对象。 std::pair's constructor 通过引用获取对象,而引用需要绑定到一个对象。因此 A_VAL 的定义成为必需的,因此链接器会抱怨。

您需要在某处定义对象(C++17 之前)

// At namespace scope
constexpr unsigned base::A_VAL;

或者切换到编译为 C++17。然后A_VAL(像所有constexpr静态成员数据一样)将隐式为一个内联变量,编译器将自行解析它的定义。

我不确定constexpr 对此有何影响,但您只是声明了静态 class 变量,但没有定义它。 IE。通常你需要在 .cpp 文件的某处有 constexpr unsigned int base::A_VAL{0x69U};