我正在使用std :: make_unique(std :: make_shared)来实例化一个带参数的类。该参数应由静态常数初始化。
如果此静态常量由常量初始化程序(内部类定义)初始化并且未根据标准(外部类定义)的要求正确定义,则优化-O0显示链接器错误但任何优化级别(-O2,-O3,-Os)没有显示任何错误。编译的程序运行。
我知道缺少的静态常量定义是一个错误。感谢this question和this question。
如果我删除行“size_t const Foo :: bufSize;”,为什么链接器错误只显示-O0?这是gcc中的错误吗?
我已经用以下代码测试了这个代码:用于x86 Windows的GCC 5.3.0(MinGW)用于x86 Linux的GCC 6.3.0(Debian Stretch)
我的简化示例代码:
#include <iostream>
#include <cstdint>
#include <memory>
class Bar {
public:
Bar(size_t const size)
: barSize(size) { std::cout << "Bar::Bar: Size: " << barSize << std::endl; }
~Bar(void) = default;
private:
size_t barSize;
};
#define USE_CONST_INITIALIZER 1
class Foo {
public:
#if (USE_CONST_INITIALIZER == 1)
static size_t const bufSize = 4096U;
#else
static size_t const bufSize;
#endif
Foo(void)
: spBuffer(std::make_unique<Bar>(Foo::bufSize)) // -Os compiles and links, -O0 shows linker error
//: spBuffer(new Bar(Foo::bufSize)) // no errors, -Os and -O0 compiles and links
{
std::cout << "Foo::Foo: constructed." << std::endl;
}
~Foo(void) = default;
private:
std::unique_ptr<Bar> spBuffer;
};
#if (USE_CONST_INITIALIZER == 1)
size_t const Foo::bufSize; // This definition (btw. required by standard) is essential if compiled with -O0
#else
size_t const Foo::bufSize = 4096U;
#endif
int main(void) {
std::cout << "Hello world!" << std::endl;
Foo foo;
return 0;
}
变量Foo::bufSize
是odr-used,因为将它传递给make_unique
函数会导致它通过转发引用传递,这需要它有一个地址。 (请注意,编写(int)Foo::bufSize
会阻止它被使用。)
但是,由于编译器可以看到make_unique
的定义并且知道Bar
按值获取其构造函数参数,在某个优化级别,它可能足够聪明地消除对make_unique
的调用,只需在对构造函数的调用中内联值4096U
Bar
,或甚至可能完全消除Bar
对象,因为barSize
的初始化是一个死的存储。在这种情况下,它不需要发出对Foo::bufSize
的引用,也不会发生链接器错误。
编译器和链接器不需要告诉您违反了ODR,因为ODR违规会导致程序“格式错误,无需诊断”。