Should 'Const' and 'Constexpr' Variables in Headers Be 'Inline' to Prevent Odr Violations

Should `const` and `constexpr` variables in headers be `inline` to prevent ODR violations?

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

using constants in header file with ODR compliance

This code is OK. The full paragraph (C++14 [basic.def.odr/6.2]) is:

in each definition of D, corresponding names, looked up according to 3.4, 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 has the same literal type in all definitions of D, and the object is initialized with a constant expression, and the object is not odr-used, and the object has the same value in all definitions of D; and

This usage does match all of the conditions in the "except ... and ... and ..." part:

  • The name CONSTANT does in fact refer to a non-volatile const object with internal linkage
  • It has the same literal type in all definitions of f().
  • It is initialized with a constant expression 2.
  • It is not odr-used.
  • It has the same value in all definitions of f().

The point "It is not odr-used" is supposed to mean "It is not odr-used within f()" -- i.e. it doesn't break f() if you happen to odr-use CONSTANT elsewhere in the program.

Is it an ODR violation to have an inline templated function have different behavior due to if constexpr?

ODR is not based on "templated functions"; it is based on actual functions. Templates generate functions based on template parameters. Each unique set of template parameters represents a different function. Different functions generated from the same template are different functions. There are no ODR expectations between different functions, regardless of what created them.

loggingGetRef<S> is the name of a particular function. If you do something that causes loggingGetRef<S> to generate differently due to constant expressions, you have ill-formed code (no diagnostic required). But that isn't a matter of ODR violations; that's a violation of the rules of template instantiation. A template that is instantiated with a specific set of parameters must be the same in all translation units that instantiate it with those parameters. Period.

Replacing constants: when to use static constexpr and inline constexpr?

In C++17, the proper way to replace those old idioms (e.g. #define) in headers in namespace scope is to use constexpr inline variables -- and not static (which is implied: they already have internal linkage).

While typically you won't encounter ODR issues (because integer compile-time constants such as those you describe are rarely ODR-used and there is a provision for their typical usage within inline functions), it is best to mark them as inline now that we have the feature in the language and avoid all problems.

See Should `const` and `constexpr` variables in headers be `inline` to prevent ODR violations? for the technical details about it.

constexpr global constants in a header file and odr

Note: as of C++17, you can declare your variables as inline.


TL;DR: If you want to be on the (very) safe side, go with constexpr functions. It isn't inherently necessary though, and certainly won't be if you're performing trivial operations on these objects and are solely interested in their value, or simply don't use them in the dangerous scenarios listed below.

The fundamental issue is that const variables at namespace scope such as yours (generally) have internal linkage ([basic.link]/(3.2)). This implies that each translation unit compiling the corresponding header will observe a different entity (i.e. symbol).

Now imagine we have a template or inline function in a header using those objects. The ODR is very precise about this scenario - [basic.def.odr]/6:

Sample Image

"initialized with a constant expression" is certainly met, since we're talking constexpr. So is "the object has the same value in all definitions of D" if you don't monkey about.

"the object isn't odr-used" is probably the only questionable condition. Basically, it requires that you don't necessitate the variables runtime existence as a symbol, which in turn implies that

  • You don't bind it to a reference (=> you don't forward it!)

  • You don't (neither explicitly nor implicitly) take its address.

The only exception to the second rule are arrays, which can be taken the address of implicitly inside a subscript operation as long as the two above rules aren't violated for the yielded glvalue.

More precisely, odr-use is governed by [basic.def.odr]/3:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying
the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.20) 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 (4.1) is applied to e, or e is a discarded-value expression (Clause
5).

Applying l-t-r to any constexpr variable will behave as required by the first part. The second part requires that the variable be used as a value rather than an actual object; that is, it's eventually either discarded or directly evaluated, giving the above rules of thumb.

If you avoid odr-use of the variable inside inline functions, templates or the like, you're fine. But if you use the return value of a corresponding constexpr function, you won't have to worry, since prvalues are already behaving more like values/literals (not objects) and constexpr functions are inline and definitely won't violate the ODR (if you don't use constexpr variables inside there!).

constexpr variable in source file global scope

It's not required to enclose constexpr variables, declared in a source file, in an unnamed namespace. Since the end goal is to achieve internal linkage you have to remember this:

The name of an entity that belongs to a namespace scope has internal
linkage
if it is the name of

  • a variable, variable template,
  • function, or function template that is explicitly declared static; or
  • a non-template variable of non-volatile const-qualified type, unless
    • it is explicitly declared extern, or
    • it is inline or exported, or
    • it was previously declared and the prior declaration did not have internal linkage; or
  • a data member of an anonymous union.

i.e since "constexpr implies const and const on namespace scope implies internal linkage", it's superfluous to say

static constexpr int ORDER = 1;

or even

namespace {
static constexpr int ORDER = 1;
}

If you want a tangible proof of the internal linkage property, consider this compilation error

Killing Magic Numbers: const int vs constexpr int (or is there no difference in the end)

Is there any meaningful difference between the two in this case (I recall the compilering deducing that - in either case - the value does not change and actually hardcodes the 42 in the resulting loop/unrolled loop/whatever code) or does it come down to personal taste?

There won't be any difference in codegen in the case you have shown.

However, the difference is that a constexpr variable guarantees that the value is known at compile-time. See VittorioRomeo's answer.

It is also good to write constexpr if it is truly a compile-time value, for documentation purposes: when someone reads your code and sees constexpr, they automatically know it is a truly fixed value. This is important in the case the initialization is non-trivial (e.g. a call to a function).

You can also see constexpr variables as the true replacement for C macros that contained literals (e.g. #define FOO 123).

Finally, remember that constexpr implies const.

In a related issue: what if the magic number (and the thing that replaces it) were declared in a header file instead of a .ccp file - would that change things?

No. However, if you are declaring global variables in a header file, you probably want to use inline (available in C++17) on top of constexpr, so that you only have a single entity in the program, which is an advantage to avoid ODR issues and possibly save memory and initialization time.

See Should `const` and `constexpr` variables in headers be `inline` to prevent ODR violations? for more information.

Are variable templates declared in a header, an ODR violation?

Templates get an exception from the one-definition rule, [basic.def.odr]/13:

There can be more than one definition of a [...] templated entity ([temp.pre]) [...] in a program provided that each definition appears in a different translation unit and the definitions satisfy the following requirements.

There's a bunch of requirements there, but basically if you have a variable template in a header (a variable template is a kind of templated entity) and just include that header from multiple translation units, they will have the same token-for-token identical definition (because #include), so having more than one definition is fine.

This is the same reason that you don't need to declare function templates inline in headers, but do need to declare regular functions inline.


In C++17, this wording read:

There can be more than one definition of a class type, enumeration type, inline function with external linkage ([dcl.inline]), inline variable with external linkage ([dcl.inline]), class template, non-static function template, static data member of a class template, member function of a class template, or template specialization for which some template parameters are not specified ([temp.spec], [temp.class.spec]) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements.

Note that variable template is not in that list, which was just an omission (it was very much always intended to work). This was CWG 2433, adopted in 2019, but as a defect report (DR) so I wouldn't consider it as counting as a C++20 change. This is the DR that introduced the "templated entity" bullet I cited earlier (rather than listing out several different kinds of templated entity manually, and missing one).



Related Topics



Leave a reply



Submit