The rule is [basic.def.odr]/4:
A variable x
whose name appears as a potentially-evaluated expression ex
is odr-used by ex
unless applying the lvalue-to-rvalue conversion to x
yields a constant expression that does not invoke any non-trivial 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 ([conv.lval]) is applied to e
, or e
is a discarded-value expression ([expr.prop]).
The first part is obviously satisfied (FOO1
is constexpr
so the lvalue-to-rvalue conversion does yield a constant expression without invoking non-trivial functions), but is the second?
We're constructing a map
. The relevant constructor there takes an initializer_list<value_type>
, which is to say an initializer_list<pair<const string, int>>
. pair
has a bunch of constructors, but the one that would be invoked here is:
template <class U1, class U2>
constexpr pair(U1&& x, U2&& y); // with U1 = char const*&, U2 = int
The important part here is that we're not directly constructing a string
, we're going through this converting constructor of pair
, which involves binding a reference to FOO1
. That's an odr-use. There's no lvalue-to-rvalue conversion here, nor is this a discarded-value expression.
Basically, when you take the address of something, that's an odr-use - it has to have a definition. So you have to add a definition:
constexpr char const* Foo::FOO1;
Note that, on the other hand, this:
std::string s = FOO1;
would not be an odr-use. Here we're directly invoking a constructor taking a char const*
parameter, which would be an lvalue-to-rvalue conversion.
In C++17, we got this new sentence in [dcl.constexpr]:
A function or static data member declared with the constexpr specifier is implicitly an inline function or variable ([dcl.inline]).
This doesn't change anything about odr-use, FOO1
is still odr-used in your program. But it does make FOO1
implicitly an inline variable, so you don't have to explicitly add a definition for it. Pretty cool.
Note also that just because a program compiles and links does not mean that a variable that lacks a definition was not odr-used.
So it indicates that both optimizations (-O) and LinkTimeOptimization (-flto) would affect ODR-use rule?
They do not. Optimizations are cool like that.