Consider the following two translation units:
// foo.cpp
#include <iostream>
class Foo {
public:
virtual ~Foo() = default;
virtual void bar();
private:
static int _baz;
};
static int f() {
std::cout << "f called\n";
return 42;
}
int Foo::_baz = f();
void Foo::bar() {
std::cout << "Baz::bar called\n";
}
and
// main.cpp
#include <iostream>
int main() {
std::cout << "main called\n";
}
When compiling both translation units into a single executable (e.g. with g++ -std=c++17 main.cpp foo.cpp, choice of optimization level or ordering of the two cpp files doesn't matter), the resulting executable prints
f called
main called
regardless of which of the three major compilers GCC, clang and MSVC was used to compile it. You can see the behaviour for yourself on wandbox.
My question is: Does the standard guarantee that Foo::_baz will be initialized (and thus f will be called) even if the entire class Foo isn't used in the program at all?
I believe this to be the case; my reasoning goes as follows:
According to [basic.start.dynamic]/4, Foo::_baz doesn't have to be initialized before the first statement of main is executed, but
if [the initialization] is deferred, it strongly happens before any non-initialization odr-use of any non-inline function or non-inline variable defined in the same translation unit as the variable to be initialized.
Here, "non-initialization odr-use" is defined as
[...] an odr-use ([basic.def.odr]) not caused directly or indirectly by the initialization of a non-local static or thread storage duration variable
in [basic.start.dynamic]/3. But as per [basic.def.odr]/7,
a virtual member function is odr-used if it is not pure.
from which I conclude that the definition of Foo::bar is a non-initialization odr-use of a non-inline function defined in the same translation unit as Foo::_baz, and thus Foo::_baz will be initialized.
What I find odd about this line of reasoning is that the deferred initialization of Foo::_baz would need to happen before the odr-use of Foo::bar, i.e. before Foo::bar is defined (wtf?!) since the definition alone is an odr-use. This makes me think my reasoning might be flawed.
So again: Does the standard guarantee that Foo::_baz will be initialized (and thus f will be called) even if nothing in its translation unit is ever used in the program at all? If yes, is there something we can say about when this will happen (given the weird ordering constraint that it has to happen before the virtual member function is defined), and if no, where's the error in my reasoning?
int Baz::_i = f();, but if the variable is not actually used anywhere, and there are no side-effects, then the optimiser could remove it. Proving either of those conditions would be extremely difficult, so I would always expect the initialisation to take place. Either way, it doesn't seem to have anything to do with virtual functions. - user2100815f. If my reasoning as written in the question is sound thenfalways has to be called, but I'm not sure I've missed something. - Corristo