如何处理clang中的全局构造函数警告?

33

当使用-WeverythingWglobal-constructors时,Clang会对静态对象的构造函数进行警告。

warning: declaration requires a global constructor
      [-Wglobal-constructors]
A A::my_A; // triggers said warning
     ^~~~
为什么这个警告很重要,应该如何处理?
简单的示例代码:
class A {
  // ...
  static A my_A;
  A();
};

A A::my_A; // triggers said warning

3
请给我们看一下警告? - jrok
这个在一个 .h 文件里吗? - yngccc
2
@user93353 不是的。它是“静态的”。它在“类A”中被声明,但它不驻留在每个“类A”中。 - Drew Dormann
4
@DrewDormann - 这个程序编译通过 struct A { static A s; }; A A::s; int main() { A a; a.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s; } - 这几乎像一个 C++ 玩笑。为什么会有人想在一个对象内部放置静态对象? - user93353
4
一个static对象是属于类而不是类的个别对象的一部分,即不在其内部。但是,您可以像访问对象的成员一样访问它:A::sA().s引用同一个(唯一的)对象(无论其是否为相同类型的A)。具有相同类型的static成员是实现单例模式的一种方式。 - Walter
显示剩余2条评论
3个回答

29

这里有一个更简单的情况会触发相同的警告:

class A {
public:
  // ...
  A();
};

A my_A; // triggers said warning


test.cpp:7:3: warning: declaration requires a global constructor [-Wglobal-constructors]
A my_A; // triggers said warning
  ^~~~
1 warning generated.

这是完全合法和安全的C++。

然而,对于每个不平凡的全局构造函数,您的应用程序启动时间都会受到影响。警告只是一种让您了解这个潜在性能问题的方式。

您可以使用-Wno-global-constructors禁用警告。或者您可以改为像这样的延迟初始化方案:

A&
my_A()
{
    static A a;
    return a;
}

完全避免了这个问题(并抑制了警告)。


22
你确定性能是主要问题吗?我认为可能会出现更大的问题——因为在C++中全局/静态初始化顺序是未定义的,非平凡的全局对象可能会在其构造函数中引入对另一个全局对象的依赖关系,从而导致未定义的行为。 - SomeWittyUsername
2
好的,谢谢。我觉得我被“全局构造函数”的措辞搞混了。也许像“启动时构建”这样的措辞更合适。 - Walter
@icepack 很好的观点。虽然我还没有遇到过问题,但这确实可能会在我的代码中发生... - Walter
3
@icepack: 是的,我确定。全局构造函数的顺序部分被定义。在同一翻译单元中出现的构造函数按照它们出现的顺序构造。翻译单元之间的排序是未指定的。是的,这可能会引起问题。但不是引入此警告的动机。 - Howard Hinnant
如果clang/gcc能告诉您如何在全局禁用警告的同时,抑制一个特定调用点的警告,那将是很好的。 我经常发现抑制单个调用点非常不符合惯例,并且因情况而异。 尽管如此,这个小技巧让我解决了问题,所以谢谢。 - Necro
这是完全合法和安全的C++ - 静态初始化顺序惨败 获胜!C++委员会让这个问题搁置了30年。委员会试图半妊娠。有时候,翻译单元中发生的事情对他们很重要(比如ODR),有时候则不重要(比如SIOF)。 (或者他们认为所有程序都由一个单一的目标文件组成...)。 - jww

5

@Howard Hinnant提供的解决方案避免了全局构造函数,但仍然存在退出时间析构函数。

可以通过选项-Wexit-time-destructors找到它。

因此,理想的解决方案可以基于http://src.chromium.org/svn/trunk/src/base/basictypes.h中的CR_DEFINE_STATIC_LOCAL。

A& my_A()
{
    static A &a = *new A;
    return a;
}

2
这段代码将会导致内存泄漏工具发出警告。 - David Faure
1
当然!对于这段代码,析构函数不会被调用。 - Maxim Kholyavkin
@DavidFaure,在A::my_A()内部持有一个static unique_ptr<A>(而不是static A&)是否可以避免内存泄漏? - Walter
1
@Walter:定义{ static A a; return a; }将会给出您建议的使用unique_ptr相同的结果。 - hauron
1
@hauron Speakus提出的解决方案与使用静态对象不同之处在于C++ FAQ的这一段中有所描述。更好但更复杂的解决方案(FAQ称之为Nifty Counter Idiom)缺少解释,但我猜想这是指用于初始化std::cout和其它相关对象的技术(参见std::ios_base::Init)。 - Max Truxa
显示剩余2条评论

3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接