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.
As you have pointed out, examples one and third does indeed violate ODR as per [basic.def.odr]/12.2.1
[..] in each definition of D, corresponding names, looked up according to [basic.lookup], shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution and after matching of partial template specialization, except that a name can refer to
a non-volatile const object with internal or no linkage if the object
- is not odr-used in any definition of D, [..]
Is this reasoning correct?
Yes, inline variables with external linkage are guaranteed to refer to the same entity even when they are odr-used as long all the definitions are the same:
[dcl.inline]/6
An inline function or variable shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case ([basic.def.odr]). [..] An inline function or variable with external linkage shall have the same address in all translation units.
The last example is OK because it meets and don't violate the bold part of the above.
are there any disadvantages whatsoever of always marking const and constexpr variables in headers as inline?
I can't think of any, because if we keep the promise of having the exact same definition of an inline variable with external linkage through TU's, the compiler is free to pick any of them to refer to the variable, this will be the same, technically, as having just one TU and have a global variable declared in the header with appropriate header guards
Best Answer
This is in reference to inline functions in different translations units. In your example they are both in the same translation unit.
This is covered in the draft C++ standard
3.2
One definition rule [basic.def.odr] which says:and includes the following bullet: