This question is a followup question to C++17: still using enums as constants?.
Legacy constants come in several forms, notably:
#define CONSTANT x
enum { CONSTANT = x };
const /*int/unsigned/whatever*/ CONSTANT = x;
A comment about static constexpr and inline constexpr constants as a replacement got me thinking on the subject of updating our many, many legacy constants (particularly #define constants).
As I understand, an inline constexpr value is basically just substituted in place, like an inlined function (which I've been shown to be wrong about). Conversely, a static constexpr value is stored as part of the binary in a separate area. Assuming I understand correctly, when should one be preferred over the other? My hunch is that, for integral constants, inline constexpr will generally be preferred.
Your go-to for global constants in C++17 should just be:
inline constexpr int CONSTANT = 42;
This gets you a nice, first-class variable that you can use in constant expressions and that won't have ODR-issues. You can take references to it.
Macros bring in the problem of... being macros. Enums are limited to integral types. With constexpr variables, you can have them of any literal type. In C++20, you'll very likely be able to just go wild and write:
inline constexpr std::vector<int> small_primes = {2, 3, 5, 7, 11};
inline constexpr std::string cool_name = "Barry";
It is the only option that allows this.
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.
Related
It seems that the rules for the compile-time keywords: constexpr, consteval and constinit are defined well enough for compilers to warn if you misapply the label.
It would make sense that (much like inline) the compiler can, in all places, apply rules to determine if, in fact, code could have one of the compile-time keywords applied and, be forced, per language specification, to compile as much as possible as if the compile-time keywords had been applied.
Or, at a minimum, if a compile-time keyword is applied to a function and the code would have qualified with had the correct compile-time keywords been applied. Compile that function, as if all the functions called had the correct compile-time keywords.
Here is a simple example:
#include <tuple>
consteval std::tuple<int, int> init1(int x, int y)
{
return {x,y};
}
std::tuple<int, int>& foo1()
{
static constinit std::tuple<int, int> x=init1(1,2);
return x;
}
std::tuple<int, int> init2(int x, int y)
{
return {x,y};
}
std::tuple<int, int>& foo2()
{
static std::tuple<int, int> x=init2(1,2);
return x;
}
static std::tuple<int, int> x3=init2(1,2);
std::tuple<int, int>& foo3()
{
return x3;
}
see it here: https://godbolt.org/z/KrzGfnEo7
Note that init1/foo1 are fully specified with compile-time keywords and there is no need for a guard variable, since it is initialized completely (not just 0 filled).
The question is about why the compiler doesn't do the same in the cases of init2/foo2 or x3/foo3? Or more precisely why the compiler is allowed NOT to initialize fully at compile time.
EDIT (as per comment) see (Why do we need to mark functions as constexpr?) constexpr would be required. I am concerned more with cases of consteval and constinit. Could the specification be modified to require trying to resolve code at compile-time and not failing because of the absence of constexpr? This would allow interface contracts to just use constexpr as per the related question.
Or, at a minimum, if a compile-time keyword is applied to a function and the code would have qualified with had the correct compile-time keywords been applied.
The basis of your question is the assumption that these keywords are just variations on a theme, that a function which could have some of them ought to have a specific one based on the properties within the function alone.
That's not reasonable.
For functions, any function which could be constexpr could also be consteval. The difference is that one can be called during constant expression evaluation, and the other must be called only during constant expression evaluation. This is not an intrinsic property of the function definition; it is about how the function is to be used.
The compiler should not override the decision of a programmer to be able to use a function at runtime just because the function definition just so happens to also be legit for consteval.
It should also be noted that there is the requirements of a function definition for a consteval function are the same as for a constexpr function.
It is also not possible to know if a function definition ought to be constexpr or not. Remember: while a constexpr function explicitly forbids certain constructs from appearing in the definition, it also permits you to have certain constructs in the definition that aren't allowed to be evaluated during constant evaluation... so long as you never actually allow those code paths to be evaluated during constant evaluation. So there are many function definitions that just so happen to be valid for constexpr even if the programmer didn't intend for them to be evaluated at compile-time.
Lambdas get implicit constexpr definitions only because space in a lambda expression is at a premium.
For variables, implicit constexpr makes even less sense. A variable ought to be constexpr if you intend to use it in a constant expression later on. This requires it to have an initializer that is a constant expression. Implicit constexpr is a problem because you might start relying on the implicit constexpr rule, then change the initializer to no longer be a constant expression and get a strange error in the place where you used its constexpr properties.
It's better to make the user be explicit up-front than to misjudge what the user was trying to do and make a user think something is supposed to be valid when it wasn't intended.
And constinit is solely about ensuring that a user never accidentally uses a non-constant expression to initialize the variable. Making it implicit would make absolutely no sense, as changing the expression to no longer be a constant expression wouldn't cause compilation to fail. Compilers are smart enough to know when a variable is initialized with a constant expression and can decide how to handle that on its own.
GNU C and C++ offer the const and pure function attributes.
From the gnu online docs (emphasis mine):
In GNU C and C++, you can use function attributes to specify certain function properties that may help the compiler optimize calls or check code more carefully for correctness. For example, you can use attributes to specify that a function never returns (noreturn), returns a value depending only on the values of its arguments (const), or has printf-style arguments (format).
Where the const attribute seems to be a subset to pure, also taken from the gnu docs:
The const attribute imposes greater restrictions on a function’s
definition than the similar pure attribute. Declaring the same
function with both the const and the pure attribute is diagnosed.
With C++ 11, the constexpr specifier was added.
When applied to functions, is there a difference between the const attribute and the constexpr specifier? Does GCC apply different optimizations?
A similar sounding question is Difference between `constexpr` and `const` . But I think this is not a duplicate. My question is specifically about the function attribute const, which seems to have overlapping functionality with constexpr.
When applied to functions, is there a difference between the const attribute and the constexpr specifier?
There are differences.
Firstly, C does not have constexpr, so you cannot take advantage of it in that language.
Calls to constexpr function can be constant expressions. As such, their result can be used for example as the size of an array. The GNU attributes cannot be used to achieve the same (ignoring GCC VLA language extension).
Constexpr functions are good for taking advantage of pre-calculation at compile time. The GNU attributes are still useful for allowing the compiler take advantage of runtime constness. For example, let's say there is a function that cannot be constexpr - perhaps because it calls a non-constexpr function. But we may know that every call to the function produces same output with no side-effects. Const attribute allows the compiler to not repeat redundant calls.
Another difference is that constexpr functions must be defined inline. Non-constexpr functions don't need to be defined inline.
There is very little overlap between the them. Here is a function that is gnu::const (and therefore gnu::pure) but cannot be constexpr:
[[gnu::const]] int f() {
struct s {
int m;
};
union {
s a;
s b;
};
a.m = 1;
return b.m;
}
You cannot read from the common initial subsequence of a union in a constant expression, so this function never produces a constant expression.
The following is a function that is constexpr but cannot be gnu::pure (and therefore cannot be gnu::const):
constexpr void f(int & x) {
x = 1;
}
This writes to memory, which gnu::pure functions cannot do.
constexpr just means that it can be called at compile time. This has overlap with gnu::const and gnu::pure in that you cannot write to global variables or access volatile memory (all IO operations access volatile memory from C++'s point of view), but they each have other restrictions that make them distinct.
In C++11, constexpr was much more limited. You could not write to any reference parameters, so I think C++11 constexpr was a strict subset of gnu::pure. The following is an example of a C++11 constexpr function that cannot be marked gnu::const, however:
[[gnu::pure]] constexpr int f(int const & x) {
return x;
}
Another difference is that a __attribute__ ((const)) function must return the same output value for the same argument (if I am not wrong; it's the first time I see this attribute ;). That does not need to hold for constexpr functions.
An academic example:
constexpr int rand(int n)
{
std::string_view sv(__TIME__);
return sv.back() % n;
}
std::array<int, rand(10) + 1> a; // exemplary usage
Though rand is constexpr, it may produce different output for the same input argument.
As far as I can see, a very common situation is something like
template<int i> class Class
{
public:
static constexpr int I = i;
static constexpr int J = constexprFunction(i);
// further Class implementation
};
Almost as common I see the mistake (in fact most of my questions here are because I forgot it and did not know, what the proper question had been) to forget the additional definition if the member are odr-used:
template<int i> constexpr int Class<i>::I;
template<int i> constexpr int Class<i>::J;
Now I read cppreference: Definitions and ODR and cppreference: static members, which state, that this is deprecated for C++17. This seems great to me, because it avoids a lot of errors. But there are other questions, that came up:
1) Has this change other reasons than making the additional definitions useless? (See also last paragraph of this question)
2) In the last example of cppreference: static members it seems also to apply on const static member - but the rule states only the constexpr member. Will it apply on const static member or not?
3) All examples I found were using a simple definition like Class::I - does it all hold also for the situation at Class:J with constexpr functions?
A brief state what the best practices are before C++17 and with C++17 would be great. All in all this seems a very tricky change to me, because it will make a lot of code, which was "ill-formed non diagnostic required" before, to good code (as far as I understand...). And consequently there will be code produced, that is still "ill-formed non diagnostic required" with older (pre 17) compiler - but these will not complain, as long as no odr-use is required.
Edit: Corrected the text, suggested by Aaron McDaid.
This change is due to the inline variables proposal (P0386). static constexpr will imply inline, making definitions redundant.
In Annex D, add a new subclause, “Redeclaration of static constexpr data members”, D.X, with the following content:
For compatibility with prior C++ International Standards, a constexpr static data member may be redundantly redeclared outside the class with no initializer. This usage is deprecated.
[Example:
struct A {
static constexpr int n = 5; // definition (declaration in C++2014)
};
const int A::n; // redundant declaration (definition in C++2014)
—end example]
Regarding to your questions:
Has this change other reasons than making the additional definitions useless?
In essence, no. Yet it has additional uses besides the one you noted (see this question). This proposal was controversial because it might encourage the use of a mutable global state.
Will it apply on const static member or not?
No. Unless you annotate it as inline.
does it all hold also for the situation at Class:J with constexpr functions?
Yes. The proposal deals with linking but does not affect initialization rules.
As far as I can see, a very common situation is something like
template<int i> class Class
{
public:
static constexpr int I = i;
static constexpr int J = constexprFunction(i);
// further Class implementation
};
Almost as common I see the mistake (in fact most of my questions here are because I forgot it and did not know, what the proper question had been) to forget the additional definition if the member are odr-used:
template<int i> constexpr int Class<i>::I;
template<int i> constexpr int Class<i>::J;
Now I read cppreference: Definitions and ODR and cppreference: static members, which state, that this is deprecated for C++17. This seems great to me, because it avoids a lot of errors. But there are other questions, that came up:
1) Has this change other reasons than making the additional definitions useless? (See also last paragraph of this question)
2) In the last example of cppreference: static members it seems also to apply on const static member - but the rule states only the constexpr member. Will it apply on const static member or not?
3) All examples I found were using a simple definition like Class::I - does it all hold also for the situation at Class:J with constexpr functions?
A brief state what the best practices are before C++17 and with C++17 would be great. All in all this seems a very tricky change to me, because it will make a lot of code, which was "ill-formed non diagnostic required" before, to good code (as far as I understand...). And consequently there will be code produced, that is still "ill-formed non diagnostic required" with older (pre 17) compiler - but these will not complain, as long as no odr-use is required.
Edit: Corrected the text, suggested by Aaron McDaid.
This change is due to the inline variables proposal (P0386). static constexpr will imply inline, making definitions redundant.
In Annex D, add a new subclause, “Redeclaration of static constexpr data members”, D.X, with the following content:
For compatibility with prior C++ International Standards, a constexpr static data member may be redundantly redeclared outside the class with no initializer. This usage is deprecated.
[Example:
struct A {
static constexpr int n = 5; // definition (declaration in C++2014)
};
const int A::n; // redundant declaration (definition in C++2014)
—end example]
Regarding to your questions:
Has this change other reasons than making the additional definitions useless?
In essence, no. Yet it has additional uses besides the one you noted (see this question). This proposal was controversial because it might encourage the use of a mutable global state.
Will it apply on const static member or not?
No. Unless you annotate it as inline.
does it all hold also for the situation at Class:J with constexpr functions?
Yes. The proposal deals with linking but does not affect initialization rules.
Here are two line of code:
static const double RYDBERG_CONST_EV = 13.6056953;
static const char CHAR_H_EDGE = '-';
The second line compiles without errors, the first line does not compile. (Error: 'constexpr' needed for in-class initialization of static data member...)
The solution is apparently to add the keyword constexpr before the type. This is required because double is not an "integral type". But why does the behaviour differ between integer and floating point types?
I don't believe that there is a good reason for this except that it has grown historically.
The exception for integral types was desirable in pre-C++11 because people wanted to use them as array sizes. This goes along with the other exception for integral constants being treated as constant expressions. An exception that doesn't exist for floating-point types.
const int ni = 10;
const float nf = 10.0f;
int numbers1[(unsigned) ni]; // perfectly fine in all versions of C++
int numbers2[(unsigned) nf]; // error in all versions of C++
When C++11 introduced constexpr, it could do anything the special-casing for const integral types could do and much more. And it works the same way for any literal type. So, given a superior tool, there was no need to dilate the existing rules for integral types to floating-point.
Today, the special-casing of integral types is mostly a left-over from the earlier darker days. It cannot be removed from the language because doing so would break existing code that relies on this special-casing but there would be little gains from complicating the language even further by adding more exceptions that would be entirely unneeded today thanks to constexpr. People should be expected to migrate to constexpr and not worry about the old cruft any more. I believe that this was a very reasonable decision but you could certainly argue that another decision should have been made.
Addendum
As T.C. has commented, there has been a (non)-defect report about this issue where the committee confirmed that the behavior won't be changed and people are supposed to start using constexpr.
1826. const floating-point in constant expressions
Section: 5.20 [expr.const] Status: NAD Submitter: Ville Voutilainen Date: 2014-01-04
A const integer initialized with a constant can be used in constant expressions, but a const floating point variable initialized with a constant cannot. This was intentional, to be compatible with C++03 while encouraging the consistent use of constexpr. Some people have found this distinction to be surprising, however.
It was also observed that allowing const floating point variables as constant expressions would be an ABI-breaking change, since it would affect lambda capture.
One possibility might be to deprecate the use of const integral variables in constant expressions.
Additional note, April, 2015:
EWG requested CWG to allow use of const floating-point variables in constant expressions.
Rationale (May, 2015):
CWG felt that the current rules should not be changed and that programmers desiring floating point values to participate in constant expressions should use constexpr instead of const.
For the wording, § [class.static.data]/3 says:
If a non-volatile const static data member is of integral or enumeration type, its declaration in the class
definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (5.20). A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression.
(emph mine). Note how in case of integral types the static data member can, not must, have an initialization (you can always put it outside the class definition). Plus, the only other way to have an initaliation inside a class definition is by means of constexpr.
The reasoning (IMHO) about allowing integral types (also in C++98) to be initialized in the class definition is in order to enable very simple patterns like this:
class Foo {
static const size_t arrayLen = 42;
int foo[arrayLen];
};
which without an in-body initialization would become impossible to implement.
It is known that:
static const integral type members can be initialized in class definition.
static constexpr members can be initialized in class definition
double is not an integral type and should be marked as a constexpr.
Executables produced in a machine can run in other machines where floating point representation computation is different. Integral Constant Expressions do not change.
Marking an object static says that it can be known by all observers, and making it const is saying that the value does not change. The compiler can generate a value (e.g. 314 ) and put it in the read-only section, because the range is defined in the standard.
On the other hand double is not in the standard and cannot have its ranged check and the value stored at compile-time, in class definitions. One could easily end up with having different object files with objects having a different value for that static const double, thus breaking ODR.
Here is a simple example:
struct foo
{
static char const a = 1/11; // everyone gets 0 at compile-time
};
You would say then , but this can happen for doubles and at first look, something like this
struct foo
{
static double const y=1.0/11.0; // the true value is 0.090909...
};
seems verifiable, but the representation in double in one machine will be 0.09091 in another 0.090909091 .
Using constexpr permits to say to the compiler that the input necessary to verify this is available at compile-time . However, actual evaluation can happen at run-time.
Since object files produced by C++ compilers can move to machines with different floating point representations , you have to tell that this checking must be made during compile time to ensure that consistency.
The question is a typical example of an XY-problem. Instead of asking , "why do I have to mark anything with constexpr?" a puzzle char-vs-float is given. Now the question is, "why do we have to use constexpr for non-integral types?", and here you can find your answer .