I stumbled across some peculiar behavior in an old program and am figure out why G++ and CLang++ allow it to happen. I have some global variables declared and initialized before main(). The odd part is that they are initialized through a static std::map which is being populated at the same time using the subscripting operator. Everything seems to be in the correct place as soon as main() runs, with the size of the map showing the correct number of populated items as well as the variables containing the values shown before 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;
}
Here is the result after trying both G++ and CLang (I used the flags -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
Is this something inherently allowed in C++? I even went so far as to create my own map type and got the same results but am still not sure if it's behavior I can rely on.
That is perfectly correct code, albeit a bit unusual. The standard guarantees that globals in the same TU are initialized in declaration order (so staticMap
will be constructed before the other initializations happen), and including a call to an overloaded operator in an initialization expression is nothing strange.
Actually, all that lambdas stuff is overkill, you could 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";
Or, even simpler:
const char *dummy = (
staticMap[0]="a",
staticMap[1]="b",
staticMap[2]="c",
staticMap[3]="d",
staticMap[4]="e");
In general, you can execute all the code you want before main
if you define a class for that:
class MyCode
{
MyCode()
{
// here be your code
}
};
static MyCode m;
But be careful, debugging code running before main
starts is often a hairy mess that is best avoided. In all my recent projects, almost no code is run in constructors of global variables, more often I just create the relevant "globals" as locals to main
and store a pointer to them in global variables.
This is a legal, common technique to execute some code before entering main.
In C++ only declarations are allowed at file/namespace scope, but a declaration may contain function calls (the call to std::map::operator[]
is one)
Your example is effectively identical to :
int foo()
{
std::cout << "foo"; // You could fill your map here
return 42;
}
int bar = foo(); // allowed
int main() {
return 0;
}
But a more idiomatic C++ way is to use constructors to achieve the same effect :
struct foo
{
foo()
{
std::cout << "foo"; // You could fill your map here
}
};
foo f;
int main() {
return 0;
}
And you can use foo()
to do your map insertions.
Note :
In your example all those lambdas are useless, it could be simplified to :
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";
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With