What Does It Mean to "Odr-Use" Something

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 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.



Related Topics



Leave a reply



Submit