Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

Unfortunately, I am somewhat confused about constexpr, global constants declared in header files, and the odr.

In short: Can we conclude from here

https://isocpp.org/files/papers/n4147.pdf

that

constexpr MyClass const MyClassObj () { return MyClass {}; }
constexpr char const * Hello () { return "Hello"; }

is preferable over

constexpr MyClass const kMyClassObj = MyClass {};
constexpr char const * kHello = "Hello";

for defining globals in a header file if I want to "just use" those globally declared/defined entities and do not want to think about how I use them?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
336 views
Welcome To Ask or Share your Answers For Others

1 Answer

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:

enter image description here

"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!).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...