What does it mean to "odr-use" something?
Asked Answered
C

2

115

This just came up in the context of another question.

Apparently member functions in class templates are only instantiated if they are odr-used. Could somebody explain what exactly that means. The wikipedia article on One Definition Rule (ODR) doesn't mention "odr-use".

However the standard defines it as

A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.

in [basic.def.odr].

Edit: Apparently this is the wrong part and the entire paragraph contains multiple definitions for different things. This might be the relevant one for class template member function:

A non-overloaded function whose name appears as a potentially-evaluated expression or a member of a set of candidate functions, if selected by overload resolution when referred to from a potentially-evaluated expression, is odr-used, unless it is a pure virtual function and its name is not explicitly qualified.

I do however not understand, how this rule works across multiple compilation units? Are all member functions instantiated if I explicitly instantiate a class template?

Crone answered 28/10, 2013 at 8:58 Comment(2)
Note that [basic.def.odr]/6 applies to member functions of class templates "There can be more than one definition [...]"Homebrew
"Are all member functions instantiated if I explicitly instantiate a class template?" Yes, see [temp.explicit]/8+9Homebrew
L
87

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.

Lipocaic answered 28/10, 2013 at 9:31 Comment(2)
could ODR-use has overlap with "materialized temporaries" ?Chandigarh
@v.oddou, maybe on a vague conceptual level.Phasia
L
44

In plain word, odr-used means something(variable or function) is used in a context where the definition of it must be present.

e.g.,

struct F {
   static const int g_x = 2;
};

int g_x_plus_1 = F::g_x + 1; // in this context, only the value of g_x is needed.
                             // so it's OK without the definition of g_x

vector<int>  vi;
vi.push_back( F::g_x );      // Error, this is odr-used, push_back(const int & t) expect
                             // a const lvalue, so it's definition must be present

Note, the above push_back passed in MSVC 2013. This behavior is not standard compliant - both gcc 4.8.2 and clang 3.8.0 failed, with error message: undefined reference to F::g_x

Lorrimor answered 8/8, 2017 at 0:58 Comment(12)
Is it possible to odr-use a static data member like vi.push_back( F::g_x ); in c++?Sigmund
But a rvalue could also be passed to const int& ?Could the static const member be ragarded as rvalue?Kennel
+1 for the succinct opening sentence: "In plain word, odr-used means something(variable or function) is used in a context where the definition of it must be present."Reprint
I don't understand how you compile that code. Are they in the same TU ?. If they are, F::g_x has already been defined before push_back, of course it will pass. Isn't it ?Jennine
@bigxiao"a rvalue could also be passed" Yes and a temporary object is created by the compiler and the reference bound to that object. OTOH when passing a lvalue that means passing that object referred to by the evaluation of the lvalue: when you pass a lvalue you can expect the parameter in the function to refer to the correct object. The compiler will not create a temporary. If you want a temporary, make one with operator+.Kapoor
@LewisChan Nope, here static const int g_x = 2; is a declaration but not a definition.Shiv
@Shiv I don't think so. This complains of a duplicate instantiation. I believe what you're referring to is this case instead which is where the static member isn't defined but only declared. I think const integral members are allowed to be defined inline.Esurient
@Esurient You're mixing up two things. In both of your examples foo is declared in the class, but not defined. The reason why you are getting an error in your first example is listed in [class.static.data]/4 ... The member shall still be defined in a namespace scope if it is odr-used (6.3) in the program and the namespace scope definition shall not contain an initializer. You are allowed to define them inline but neither of your examples contain the inline specifier.Shiv
@Shiv Ok, I'm a bit confused. So the declaration in this is being initialized? FWIK, initialization implies definition, no? Because that would imply that some storage has been allocated to the variable?Esurient
@Esurient See this, I just quite recently answered a similar question actuallyShiv
@Shiv That helps! Essentially my doubt was what on earth happens with the value of the initializer if it's a declaration and not a definition. Your answer clears that it's stowed away in the register whereas it is actually allocated memory here on definition.Esurient
Since the relevant rule does not require a diagnostic, MSVC is conforming even if it accepts that code.Cyprio

© 2022 - 2024 — McMap. All rights reserved.