为什么关于静态常量的链接器错误取决于优化级别?

问题描述 投票:0回答:1

我正在使用std :: make_unique(std :: make_shared)来实例化一个带参数的类。该参数应由静态常数初始化。

如果此静态常量由常量初始化程序(内部类定义)初始化并且未根据标准(外部类定义)的要求正确定义,则优化-O0显示链接器错误但任何优化级别(-O2,-O3,-Os)没有显示任何错误。编译的程序运行。

我知道缺少的静态常量定义是一个错误。感谢this questionthis 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;
}
c++
1个回答
0
投票

变量Foo::bufSize是odr-used,因为将它传递给make_unique函数会导致它通过转发引用传递,这需要它有一个地址。 (请注意,编写(int)Foo::bufSize会阻止它被使用。)

但是,由于编译器可以看到make_unique的定义并且知道Bar按值获取其构造函数参数,在某个优化级别,它可能足够聪明地消除对make_unique的调用,只需在对构造函数的调用中内联值4096U Bar,或甚至可能完全消除Bar对象,因为barSize的初始化是一个死的存储。在这种情况下,它不需要发出对Foo::bufSize的引用,也不会发生链接器错误。

编译器和链接器不需要告诉您违反了ODR,因为ODR违规会导致程序“格式错误,无需诊断”。

© www.soinside.com 2019 - 2024. All rights reserved.