How to deal with global-constructor warning in clang?
Clang警告(当使用-Weverything或Wglobal-constructors时)关于静态对象的构造函数。
1 2 3 4
| warning: declaration requires a global constructor
[-Wglobal-constructors]
A A::my_A; // triggers said warning
^~~~ |
为什么这是相关的,应该如何处理这个警告?
简单示例代码:
1 2 3 4 5 6 7
| class A {
// ...
static A my_A;
A();
};
A A::my_A; // triggers said warning |
-
我们可以看到警告了吗?
-
这是一个.h文件?
-
你在A类中有一个A的对象。这是如何工作的?这不会变成递归 - 即使对象是静态的。虽然只有一个my_A并且递归不会导致问题,但我仍然可以执行A a;然后a.mY_A.my_A.my_A.my_A等。
-
@ user93353号码static。它在class A中声明,但它不存在于每个class A中。
-
@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 static对象是class的一部分,不是该类的单个对象的一部分,即不在其自身内部。尽管如此,您可以像访问对象一样访问它:A::s和A().s引用相同(且唯一)的对象(无论是否为相同类型A)。拥有相同类型的static成员是实现单例的一种方法。
-
@ user93353?我已经看到该模式有时用于创建一组具有固定值的静态实例。从概念上讲,就像类固醇的枚举一样。通常,类构造函数是私有的,因此只有暴露的静态可供外部使用。
这是一个更简单的情况,触发相同的警告:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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禁用警告。 或者您可以更改为这样的延迟初始化方案:
1 2 3 4 5 6
| A&
my_A()
{
static A a;
return a;
} |
这完全避免了这个问题(并且抑制了警告)。
-
你确定性能是主要问题吗?我认为可能会出现更大的问题 - 由于全局/静态初始化的顺序在C ++中未定义,非平凡的全局对象可能在其构造函数中引入对另一个全局对象的依赖,从而导致未定义的行为。
-
好的谢谢。我觉得我对措辞全局构造函数感到困惑。也许像启动时的建筑这样的事情会更合适。
-
@icepack好点。这可能确实发生在我的代码中,尽管我从未遇到过问题......
-
@icepack:是的,我全局构造函数的顺序是部分定义的。出现在相同翻译单元中的那些构造器按照出现的顺序构造。未指定在翻译单元之间进行排序。是的,这可能会导致问题。但不,这不是引入此警告的动机。
-
如果clang / gcc告诉你如何抑制一个特定呼叫站点的警告以及如何全局禁用警告,那将是很好的。我经常发现抑制个人呼叫站点是非常非惯用的,并且因情况而异。这个小技巧让我解决这个问题,谢谢。
-
"这是完全合法且安全的C ++" - 静态初始化订单Fiasco获胜! C ++委员会已经让这个问题恶化了30年。该委员会正试图怀孕一半。有时翻译单元中发生的事情对他们很重要(如ODR),有时则不然(如SIOF)。 (或许他们认为所有程序都由一个目标文件组成......)。
来自@Howard Hinnant的解决方案避免使用全局构造函数,但它仍然会退出析构函数。
它可以通过选项-Wexit-time-destructors找到
所以理想的解决方案可以基于http://src.chromium.org/svn/trunk/src/base/basictypes.h中的CR_DEFINE_STATIC_LOCAL。
1 2 3 4 5
| A& my_A()
{
static A &a = *new A;
return a;
} |
-
此代码将从内存泄漏工具创建警告。
-
当然!不会为此代码调用析构函数。
-
@DavidFaure通过在A::my_A()中保持static unique_ptr(而不是static A&)可以避免内存泄漏?
-
@Walter:定义{static A a;返回;会给出你使用unique_ptr建议的相同结果
-
@hauron为什么Speakus提出的解决方案与使用静态对象不同,在C ++ FAQ的这一段中有所描述。一个更好但更复杂的解决方案(常见问题解答称为Nifty Counter Idiom)缺少解释,但我想这是指用于初始化std::cout和朋友的技术(参见std::ios_base::Init)。
-
@MaxTruxa感谢您的查找,我想解决这个问题的解决方案是将静态对象包装在shared_ptr中,因此任何依赖它的依赖项都会保存一个引用,从而避免它被破坏"直到最后一刻"。并且你可以保持你的析构函数被执行。