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 ofD
, 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 ofD
, 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 ofD
; 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-volatileconst
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:
"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 expressionex
is odr-used byex
unless applying
the lvalue-to-rvalue conversion (4.1) tox
yields a constant expression (5.20) 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 (4.1) is applied toe
, ore
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
Why Is (Void) 0 a No Operation in C and C++
Cannot Convert Parameter 1 from 'Char' to 'Lpcwstr'
"Proper" Way to Store Binary Data with C++/Stl
How to Use the _Attribute_((Visibility("Default")))
Manual for Cross-Compiling a C++ Application from Linux to Windows
C++ Streams Confusion: Istreambuf_Iterator VS Istream_Iterator
Pointing to a Function That Is a Class Member - Glfw Setkeycallback
Iter_Swap() Versus Swap() -- What's the Difference
Exclude Source File in Compilation Using Makefile
How to Encode a String to Base64 Using Only Boost
C++ Integer->Std::String Conversion. Simple Function
How to Store Array in One Column in SQLite3
Can Nullptr Be Emulated in Gcc
C++ Linker Error with Class Static Constexpr
Why Is "\" an Escape Sequence in C/C++