What does it mean to ODR-use something?
It's just an arbitrary definition, used by the standard to
specify when you must provide a definition for an entity (as
opposed to just a declaration). The standard doesn't say just
"used", because this can be interpreted diversely depending on
context. And some ODR-use doesn't really correspond to what one
would normally associate with "use"; for example, a virtual
function is always ODR-used unless it is pure, even if it isn't
actually called anywhere in the program.
The full definition is in §3.2, second paragraph, although this
contains references to other sections to complete the
definition.
With regards to templates, ODR-used is only part of question;
the other part is instantiation. In particular, §14.7 covers
when a template is instantiated. But the two are related: while
the text in §14.7.1 (implicit instantiation) is fairly long, the
basic principle is that a template will only be instantiated if
it is used, and in this context, used means ODR-used. Thus,
a member function of a class template will only be instantiated
if it is called, or if it is virtual and the class itself is
instantiated. The standard itself counts on this in many
places: the std::list<>::sort
uses <
on the individual
elements, but you can instantiate a list over an element type
which doesn't support <
, as long as you don't call sort
on
it.
Basic ODR violation: member functions in .h files
Normally, a class definition goes in an ".h" file and its member functions' definitions go in a ".cpp" file.
If you want to define member functions in the header, you need to either declare them inline
, or write them inside the class definition (which makes them implicitly inline).
Why is the const static variable odr-used here?
make_tuple
takes the argument by const int&
(since it's a constant lvalue and it takes a T&&
argument), and binding a reference to a value is an ODR-use.
Both cases are ill-formed no diagnostic required. At high optimisation levels of gcc for example, the entire program is optimised out and there are no linker errors, where at no optimisation, both statements give linker errors.
To make it not an ODR use, you can convert it to an rvalue:
// Various ways to force the lvalue-to-rvalue conversion
auto t = std::make_tuple(int(Test::VALUE));
auto t1 = foo(std::make_tuple((void(), Test::VALUE)));
auto t2 = foo(std::make_tuple(+Test::VALUE));
(So std::make_tuple
takes an int&&
of a temporary)
Or you can make the definition inline
(Easiest with constexpr
):
class Test {
public:
static constexpr int VALUE = 100;
};
Why is S::x not odr-used?
It is indeed odr-used. Your analysis is correct (and I fixed that example a while ago).
Does the comma operator odr-use its arguments?
Those are just examples for how an overloaded comma operator might be defined in a class. It's the usage of such an overload that would necessarily trigger odr-use, when you bind to the arguments.
That usage hasn't been written into your program, nor has an operator overload at all for that matter.
You're just using the built-in comma operator.
(A more interesting question might be whether the right-most operand to this operator is still odr-used, because from the wording it looks to me like it is! Do remember that an odr-use violation isn't required to generate a build error, and in certain situations it won't.).
I would argue that the cppreference page is possibly a little unclear in that regard.
Is static constexpr variable odr-used?
The rule is [basic.def.odr]/4:
A variable
x
whose name appears as a potentially-evaluated expressionex
is odr-used byex
unless applying the lvalue-to-rvalue conversion tox
yields a constant expression that does not invoke any non-trivial functions and, ifx
is an object,ex
is an element of the set of potential results of an expressione
, where either the lvalue-to-rvalue conversion ([conv.lval]) is applied toe
, ore
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.
Related Topics
How to Construct a Std::String With Embedded Values, I.E. "String Interpolation"
What Is the Windows Equivalent For En_Us.Utf-8 Locale
Benefits of Initialization Lists
Why Doesn't Adl Find Function Templates
How to Create Directory Tree in C++/Linux
Why Does Calling C_Str() on a Function That Returns a String Not Work
How Does Ifstream'S Eof() Work
Static Member Initialization in a Class Template
Cout Not Printing Unsigned Char
Why Vector≪Bool≫::Reference Doesn't Return Reference to Bool
How to Reassign the Reference in C++
How Std::Unordered_Map Is Implemented
Checking Cin Input Stream Produces an Integer
What's "Wrong" With C++ Wchar_T and Wstrings? What Are Some Alternatives to Wide Characters