用作默认参数的未优化 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};
。
我不明白为什么下面的代码在 GCC 优化的情况下编译,但在未优化时无法 link 和 "undefined reference to `base::A_VAL'"。我在做一些狡猾的事吗?这是编译器错误(从来不是)吗?这是 Ubuntu.
上的 g++ 5.4.0base.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};
。