我在一个旧程序中偶然发现了一些奇怪的行为,并弄清楚为什么G ++和CLang ++允许它发生。我在main()之前声明并初始化了一些全局变量。奇怪的是它们是通过静态std :: map初始化的,它使用下标运算符同时填充。一旦main()运行,一切似乎都在正确的位置,地图的大小显示正确的填充项数以及包含main()之前显示的值的变量。
#include <map>
#include <iostream>
static std::map<int, const char*> staticMap;
const char* const a = staticMap[0] = []()->const char* {return "a";}();
const char* const b = staticMap[1] = []()->const char* {return "b";}();
const char* const c = staticMap[2] = []()->const char* {return "c";}();
const char* const d = staticMap[3] = []()->const char* {return "d";}();
const char* const e = staticMap[4] = []()->const char* {return "e";}();
int main() {
std::cout << "# Items: " << staticMap.size() << '\n' << std::endl;
std::cout << "Values:\n";
std::cout << "\"a\" = " << a << '\n';
std::cout << "\"b\" = " << b << '\n';
std::cout << "\"c\" = " << c << '\n';
std::cout << "\"d\" = " << d << '\n';
std::cout << "\"e\" = " << e << '\n';
std::cout << std::endl;
std::cout << "Map Contents:" << std::endl;;
for (unsigned i = 0; i < 5; ++i) {
std::cout << "\t" << staticMap[i] << std::endl;
}
return 0;
}
以下是尝试G ++和CLang后的结果(我使用了标志-std = c ++ 11 -Wall -Werror -Wextra -pedantic-errors):
# Items: 5
Values:
"a" = a
"b" = b
"c" = c
"d" = d
"e" = e
Map Contents:
a
b
c
d
e
这是C ++本身允许的内容吗?我甚至创建了自己的地图类型并得到了相同的结果,但我仍然不确定它是否可以依赖它。
答案 0 :(得分:9)
这是完全正确的代码,虽然有点不寻常。该标准保证同一TU中的全局变量按声明顺序初始化(因此staticMap
将在其他初始化发生之前构造),并且在初始化表达式中包含对重载运算符的调用并不奇怪。
实际上,所有lambdas的东西都是矫枉过正的,你可以simply do:
const char* a = staticMap[0] = "a";
const char* b = staticMap[1] = "b";
const char* c = staticMap[2] = "c";
const char* d = staticMap[3] = "d";
const char* e = staticMap[4] = "e";
或者,even simpler:
const char *dummy = (
staticMap[0]="a",
staticMap[1]="b",
staticMap[2]="c",
staticMap[3]="d",
staticMap[4]="e");
一般情况下,如果为此定义一个类,则可以在main
之前执行所需的所有代码:
class MyCode
{
MyCode()
{
// here be your code
}
};
static MyCode m;
但是要小心,调试在main
启动之前运行的代码通常是一个毛茸茸的混乱,最好避免。在我最近的所有项目中,几乎没有代码在全局变量的构造函数中运行,更常见的是我只是将相关的“全局变量”创建为main
的本地变量,并在全局变量中存储指向它们的指针。
答案 1 :(得分:4)
这是在进入main之前执行某些代码的合法常用技术。
在C ++中,文件/命名空间范围内只允许声明,但声明可能包含函数调用(对std::map::operator[]
的调用是一个)
您的示例与以下内容完全相同:
int foo()
{
std::cout << "foo"; // You could fill your map here
return 42;
}
int bar = foo(); // allowed
int main() {
return 0;
}
但更惯用的C ++方法是使用构造函数来实现相同的效果:
struct foo
{
foo()
{
std::cout << "foo"; // You could fill your map here
}
};
foo f;
int main() {
return 0;
}
您可以使用foo()
进行地图插入。
注意:
在你的例子中,所有这些lambda都没用,可以简化为:
const char* const a = staticMap[0] = "a";
const char* const b = staticMap[1] = "b";
const char* const c = staticMap[2] = "c";
const char* const d = staticMap[3] = "d";
const char* const e = staticMap[4] = "e";
答案 2 :(得分:0)
在输入main()
之前,会初始化命名空间范围内的变量。全局变量的初始化可以调用任意函数,既可以生成构造函数参数,也可以从相应的构造函数中调用。退出main()
后,这些全局变量将以相反的顺序销毁。
理论上,您可以完全从这些初始化之一(或仅来自析构函数)运行实际程序。但是,这可能不是一个特别好的主意。
您发布的代码完全有效但过于复杂:无需使用lambda函数为字符串生成初始值设定项。