我今天被一个小虫咬了。
让我们考虑以下来源:
struct MyPod
{
short m_short ;
const char * const m_string ;
} ;
MyPod myArrayOfPod[] = { { 1, "Hello" } } ;
int main(int argc, char * argv[])
{
return 0 ;
}
请注意,所有值在编译时都是已知的,并且MyPod是POD。
那么,myArrayOfPod应该在编译时初始化,还是编译器会生成MyPod的默认构造函数?
可以将以下复制/粘贴错误的源复制/粘贴到main.cpp文件中:
#include <iostream>
// The point of SomeGlobalObject is for its
// constructor to be launched before the main
// ...
struct SomeGlobalObject
{
SomeGlobalObject() ;
} ;
// ...
// Which explains the global object
SomeGlobalObject oSomeGlobalObject ;
// A POD... I was hoping it would be constructed at
// compile time when using an argument list
struct MyPod
{
short m_short ;
const char * const m_string ;
} ;
// declaration/Initialization of a MyPod array
MyPod myArrayOfPod[] =
{ { 1, "Hello" }, { 2, "World" }, { 3, " !" } } ;
// declaration/Initialization of an array of array of void *
void * myArrayOfVoid[][2] =
{ { (void *)1, "Hello" }, { (void *)2, "World" }, { (void *)3, " !" } } ;
// constructor of the global object... Launched BEFORE main
SomeGlobalObject::SomeGlobalObject()
{
// The two values should be "1"
std::cout << "myArrayOfPod[0].m_short : " << myArrayOfPod[0].m_short << std::endl ;
std::cout << "myArrayOfVoid[0][0] : " << myArrayOfVoid[0][0] << std::endl ;
}
// main... What else ?
int main(int argc, char* argv[])
{
return 0 ;
}
MyPod是POD,我相信没有构造函数。只在编译时初始化。
因此,全局对象SomeGlobalObject
在构造时使用全局POD数组没有问题。
但是,在Visual C ++ 2008中,在调试模式下,执行myArrayOfPod
时未正确初始化(其所有值都为零),即使myArrayOfVoid
已正确初始化。
所以我的问题是: C ++编译器是不是应该在编译时初始化全局POD(包括POD结构)?
请注意,我知道全局变量是邪恶的,我知道无法确定在不同编译单元中声明的全局变量的创建顺序,但这是无题的:问题是关于全局变量POD初始化。
我在Ubuntu上复制/粘贴了这段代码,就g ++ 4.4.3而言,这两个数组在调试和发布模式下都已正确初始化。
此行为已报告给Microsoft,等待确认: https://connect.microsoft.com/VisualStudio/feedback/details/564844/pod-struct-global-object-initialization-uses-constructor
Visual C ++ QA回答了错误提交,引用了标准(至少n3092)。就它们而言,Visual C ++上看到的行为确实遵循标准。
尽管我的“感觉”这仍然是一个错误,但我必须承认他们比我更了解标准的事实(如果只是因为我使用语言,当他们编写语言的编译器,从而接受他们的答案。
所以,我会做我的作业,也就是说,我会从头到尾阅读n3092(一千页律师般的陈述......这是我的运气......):这个文档使用了很多明确定义的单词,如果我不知道每个单词的确切含义,那么我就无法引用一些n3092段来支持我的观点......
答案 0 :(得分:3)
根据C ++标准,3.6.2.2:非本地对象的初始化:
一起,零初始化和 调用常量初始化 静态初始化;所有其他 初始化是动态的 初始化。静态初始化 应在任何动态之前执行 初始化发生。
由于myArrayOfPod
至少乍一看是用常量表达式初始化的,所以它应该在oSomeGlobalObject
之前初始化。它不在调试中的事实可能是一个错误。您可以通过connect.microsoft.com提交错误。
答案 1 :(得分:1)
C ++语言保证使用常量表达式初始化静态存储持续时间的POD对象静态,即“在编译时”。更讽刺的是,无论如何实现静态初始化(在编译时,在运行时),都必须在之前进行任何动态初始化。例如,构造函数调用是动态初始化。
在您的情况下myArrayOfPod
是POD,整数文字是整数常量表达式,字符串文字是地址常量表达式。我会说你的myArrayOfPod
满足所有要求,因此必须静态初始化。如果您在实验中从myArrayOfPod
的构造函数中观察到未初始化的oSomeGlobalObject
,那么它必定是编译器中的错误。
答案 2 :(得分:0)
对我来说,似乎是全局变量初始化的顺序,而不是它们是否。必须是Debug模式以与发布模式不同的顺序调用全局初始化代码。
Globals引用全局...可以有一定程度的邪恶? ; - )
编辑: 据推测,如果你将两个cout放在main中,你会看到所有的全局变量都是在它到达那里时被初始化的。
答案 3 :(得分:0)
C ++编译器不应该在编译时初始化全局POD(包括POD结构)吗?
没有。他们怎么样?它们是运行时构造。即使编译器在程序的描述中构建了所有内容(堆栈上的常量值),它仍然必须在运行时初始化。
IRC,在同一个翻译单元中,全局变量的初始化顺序保证按声明的顺序排列。考虑将某个全局对象放置在文件中的位置。
如果我错了,那么订单是不确定的。此外,当我们讨论跨翻译单元的全局变量时,顺序是未定义的。其中我肯定。