首页 > 解决方案 > 用作默认参数的未优化 constexpr 的未定义引用

问题描述

我不明白为什么下面的代码在 GCC 优化下编译,但在未优化时无法链接到“未定义的对 `base::A_VAL' 的引用”。我在做一些狡猾的事情吗?这是编译器错误(从来没有)吗?这是 Ubuntu 上的 g++ 5.4.0。

基地.h:

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

派生的.h:

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

using namespace std;

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

具体.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;
    }
};

测试.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.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

标签: c++linkerc++14constexprundefined-reference

解决方案


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

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

由于operator<<标准流类按值获取整数,并且A_VAL是一个常量表达式,因此上面的调用不需要为A_VAL. 它的值只是插入。因此,GCC 不需要A_VAL像静态类成员通常需要的那样定义类外。

不优化时,GCC 很可能会初始化 pair 对象。std::pair的构造函数通过引用获取对象,并且引用需要绑定到的对象。因此,定义A_VAL成为必需,因此链接器抱怨。

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

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

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


推荐阅读