I am a bit confused by the static
in-class initialization of a const
member. For example, in the code below:
#include <iostream>
struct Foo
{
const static int n = 42;
};
// const int Foo::n; // No ODR
void f(const int& param)
{
std::cout << param << std::endl;
}
int g(const int& param)
{
return param;
}
template<int N>
void h()
{
std::cout << N << std::endl;
}
int main()
{
// f(Foo::n); // linker error, both g++/clang++
std::cout << g(Foo::n) << std::endl; // OK in g++ only with -O(1,2 or 3) flag, why?!
h<Foo::n>(); // this should be fine
}
Live example
I do not define Foo::n
(the line is commented). So, I expect the call f(Foo::n)
to fail at link time, and indeed it does. However, the following line std::cout << g(Foo::n) << std::endl;
compiles and links fine only by gcc (clang still emits a linker error) whenever I use an optimization flag such as -O1/2/3
.
- Why does gcc (tried with gcc5.2.0 and gcc 4.9.3) compile and link the code when the optimization is turned on?
- And am I correct to say that the only usage of in-class static const members is in constant expressions, such as template parameters like in the
h<Foo::n>
call, in which case the code should link?
ODR violations do not require a diagnostic, from the draft C++ standard standard section 3.2
[basic.def.odr] (emphasis mine going forward):
Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program; no diagnostic
required.
So inconsistent behavior at different optimization levels is perfectly conformant behavior.
Informally a variable is odr-used if:
its address is taken, or a reference is bound to it, and a function is odr-used if a function call to it is made or its address is taken. If an object or a function is odr-used, its definition must exist somewhere in the program; a violation of that is a link-time error.
So both f
and g
will be odr-uses and require a definition.
The relevant C++14 quote on odr-use would be from section [basic.def.odr]:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying
the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any nontrivial
functions and, if x is an object, ex is an element of the set of potential results of an expression e,
where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression [...]
The wording in C++11 is similar, the changes from C++11 to C++14 are reflected in defect report 712.
Before C++11 it is a bit more complicated but in principle the same for this case.
I suppose that the compiler performs the following actions during the optimization:
The value const static int n
is inlined everywhere. No memory is allocated for the variable n
, references to it becomes invalid. The function f()
need a reference to n
so the program is not compiled.
The function g
is short and simple. It is effectively inlined and optimized. After the optimization, the function g
does not need a reference to n
, it just returns constant value 42.
The solution is to define the variable outside the class:
struct Foo
{
const static int n;
};
const int Foo::n = 42;
Formally, ODR violations are undefined behaviour, so the compiler may exhibit any behaviour it likes. That's why the behaviour changes with optimization level and compiler- the compiler has no obligation to maintain a particular behaviour.
There is no definition at all. GCC 4.9.2 doesn't compile and link that with any flags.
Note, that:
const static int n = 42;
is a declaration and initializer, but not a definition.