constexpr position in variable declaration - c++

Usually I like to put "const" behind the type, as
int const foo1 = 123;
Now, if i want to declare a second variable (which should even be constexpr) I would like to use the same style:
int constexpr foo2 = 123;
However, this led to a warning upon running a code analysis tool (however, all compilers I tried accept it).
Does the C++ standard mandate that constexpr can only be used before the actual type?

constexpr is a decl-specifier, as are int and const.
Other decl-specifiers are for example thread_local, mutable, extern, virtual, extern, friend, typedef, consteval, constinit and inline, as well as other type-specifiers such as volatile, long, double, auto, signed, decltype(...), names of types, types introduced by class, enum, union, struct, etc.
A sequence of decl-specifiers makes up the decl-specifier-seq of a declaration. Following the decl-specifier-seq is typically a list of declarators, separated by commas, which declare the entities with the given decl-specifiers. (But for example the * in int* s; is part of the declarator, not the specifier sequence.)
I don't think there is any instance in the standard where the order of the decl-specifier-seq has any relevance. For example the following compiles without issue and should have the same meaning as in the conventional order of specifiers:
struct { double volatile mutable long x = 1; } typedef const S;
inline S thread_local constexpr *s = {};
See the post-C++20 draft of the C++ standard, section [dcl.spec]. It specifies the grammar of the decl-specifier-seq and the individual specifiers. In the rest of the section certain combinations of specifiers are forbidden, but their order is not mentioned.
So
int constexpr x;
and
constexpr int x;
are equivalent.
But what is not possible is e.g.
int* constexpr x;
The constexpr would here be part of the declarator. It could be written as
int constexpr* x;
though.
I suppose that makes putting constexpr as a rule after the type specifier confusing. It is the same rule as for const, but constexpr should only ever apply to the whole declaration, not to the type or a subtype.
I think it is likely that the code analysis tool is simply complaining about the style rather than the functionality of the syntax. It is unusual to place constexpr behind the type specifiers, as it is unusual to put e.g. const between the two long in long long, and if the specifiers are in uncommon order it may become hard to read the code.

Related

Would it be sufficient for constexpr, consteval, and constinit to be definitions instead of keywords?

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.

Can I declare multiple functions with comma?

Can we use , operator to declare multiple functions once ?
This code compiles on gcc:
void f(), g();
void f() {}
void g() {}
int main() {
f();
}
Is it standard or a particularity of the compiler ?
It's standard. Look at all the things you can declare:
int a, *pa, f(int), *g(int);
This is a single declaration, with a single type specifier int and four declarators. Note that the two variable declarations there have different types, despite having the same type specifier. Same deal with the two function declarations. The type specifier only covers the innermost nugget of the type; the individual declarators wrap it in things like pointers or function-ness.
The function definition int f() { ... } is unique and unusual. While it serves to both declare and define a function, and involves some of the same syntactic constructs as declarations, it is syntactically separate from a declaration. Since a declaration is the thing that gets to have a list of declarators, the function definition syntax doesn't inherit that behavior. So you can declare multiple functions in a single declaration, but a function definition is not a declaration (despite the fact that it declares a function!) so you can only do one at a time.
Editorializing a little: This facet of the syntax, with type information spread between the type specifier and the declarator, is one of the core awfulnesses of C and C++. It dates back to the earliest days of C, when the focus of the language meant that most variables were ints and the type was actually the most optional part of a declaration. Most of us have experienced this in the form of int* a, b only making one pointer, but things like the parentheses that crop up when declaring function pointers, and the overall spiral-ness of complex type definitions, also spring from that essential, never-corrected mistake from the early 1970s.

Why is "inline" required on static inline variables?

C++17 allows static member variables to be defined thus:
class X {
public:
static inline int i = 8;
};
What is the rationale behind requiring the inline specification? Why not simply allow programmers to write
static int i = 8;
in the class?
Without inline, it's explicitly stated as only a declaration. As specified in [class.static.data]/2
The declaration of a non-inline static data member in its class
definition is not a definition and may be of an incomplete type other
than cv void. The definition for a static data member that is not
defined inline in the class definition shall appear in a namespace
scope enclosing the member's class definition.
The rationale is most probably to keep legacy code intact and valid. Recall that we could initialize integral constants in the class definition itself since about forever. But odr-using them still required an out-of-class definition in some translation unit.
So to makes such variables implicitly inline could be problematic in existing codebases. The committee is always thinking about backwards compatibility when core language features are added.
For instance, consider this valid C++03 class definition:
struct foo {
static const int n = 3;
double bar[n];
};
n can be used as a constant expression to define the extent of bar, and it's not considered an odr-use. Nowadays we'd write it as constexpr1, however that above is still valid. But there may be cases were n would have to be odr-used (imagine its address taken, or a reference bound to it, etc). They are probably not many, and probably not common, but certain API's have crazy requirements that would end up necessitating this
const int foo::n;
to appear in some translation unit.
Now, if static inline int i = 8; was suddenly implicitly inline, the definition above (that is in an existing code base) would be an odr-violation. Now previously well-formed code, is ill-formed. So it's best to allow only explicit inline to take effect here, since only new code will actually have it.
1 One could argue that static constexpr variables may have the same issue (and yet they are implicitly inline). But IIRC their original wording allowed this change without potentially breaking existing code. It was essentially already "inline" by everything but name.

Why can you initialize a static const variable inline but not a plain static (C++)

If I were to do this
class Gone
{
public:
static const int a = 3;
}
it works but if do
class Gone
{
public:
static int a = 3;
}
it gives a compile error. Now I know why the second one doesn't work, I just don't know why the first one does.
Thanks in advance.
This trick works only for constant compile-time expressions. Consider the following simple example:
#include <iostream>
class Foo {
public:
static const int bar = 0;
};
int main()
{
std::cout << Foo::bar << endl;
}
It works just fine, because compiler knows that Foo::bar is 0 and never changes. Thus, it optimizes the whole thing away.
However, the whole thing breaks once you take the address of that variable like this:
int main()
{
std::cout << Foo::bar << " (" << &Foo::bar << ")" << std::endl;
}
Linker sends you to fix the program because compile-time constants don't have addresses.
Now, the second case in your example doesn't work simply because a non-constant variable cannot be a constant compile-time expression. Thus, you have to define it somewhere and cannot assign any values in initialization.
C++11, by the way, has constexpr. You can check Generalized constant expressions wiki (or C++11 standard :-)) for more info.
Also, be careful - with some toolchains you will never be able to link program as listed in your first example when optimizations are turned off, even if you never take an address of those variables. I think there is a BOOST_STATIC_CONSTANT macro in Boost to work around this problem (not sure if it works though because I reckon seeing linkage failures with some old gcc even with that macro).
The static const int declaration is legal because you're declaring a constant, not a variable. a doesn't exist as a variable - the compiler is free to optimize it out, replacing it with the declared value 3 anywhere a reference to Gone::a appears. C++ allows the static initialization in this restricted case where it's an integer constant.
You can find more details, including an ISO C++ standard citation here.
Initialization of variables has to be done at the point of definition, not the point of declaration in the general case. Inside the class brackets you only have a declaration and you need to provide a definition in a single translation unit*:
// can be in multiple translation units (i.e. a header included in different .cpp's)
struct test {
static int x; // declaration
static double d; // declaration
};
// in a single translation unit in your program (i.e. a single .cpp file)
int test::x = 5; // definition, can have initialization
double test::d = 5.0; // definition
That being said, there is an exception for static integral constants (and only integral constants) where you can provide the value of the constant in the declaration. The reason for the exception is that it can be used as a compile-time constant (i.e. to define the size of an array), and that is only possible if the compiler sees the value of the constant in all translation units where it is needed.
struct test {
static const int x = 5; // declaration with initialization
};
const int test::x; // definition, cannot have initialization
Going back to the original question:
Why is it not allowed for non-const integers?
because initialization happens in the definition and not declaration.
Why is it allowed for integral constants?
so that it can be used as a compile-time constant in all translation units
* The actual rules require the definition whenever the member attribute is used in the program. Now the definition of used is a bit tricky in C++03 as it might not be all that intuitive, for example the use of that constant as an rvalue does not constitute use according to the standard. In C++11 the term used has been replaced with odr-used in an attempt to avoid confusion.
A static const is defined in the class definition since everybody that uses the code need to know the value at compile time, not link time. An ordinary static is actually only declared in the class definition, but defined once, in one translation unit.
I seem to recall that originally (ARM) it was not allowed, and we used to use enum to define constants in class declarations.
The const case was explicitly introduced so as to support availability of the value in headers for use in constant expressions, such as array sizes.
I think (and please comment if I have this wrong) that strictly you still need to define the value:
const int Gone::a;
to comply with the One Definition Rule. However, in practice, you might find that the compiler optimises away the need for an address for Gone::a and you get away without it.
If you take:
const int* b = &Gone::a;
then you might find you do need the definition.
See the standard, $9.4.2:
ISO 1998:
"4 If a static data member is of const integral or const enumeration
type, its declaration in the class definition can specify a
constantinitializer which shall be an integral constant expression
(5.19). In that case, the member can appear in integral constant
expressions within its scope. The member shall still be defined in a
namespace scope if it is used in the program and the namespace scope
definition shall not contain an initializer."
Draft for c++11:
"3 If a static data member is of const effective literal type, its
declaration in the class definition can specify a constant-initializer
brace-or-equal-initializer with an initializer-clause that is an
integral constant expression. A static data member of effective
literal type can be declared in the class definition with the
constexpr specifier; if so, its declaration shall specify a
constant-initializer brace-or-equal-initializer with an
initializerclause that is an integral constant expression. In both
these cases, the member may appear in integral constant expressions.
The member shall still be defined in a namespace scope if it is used
in the program and the namespace scope definition shall not contain an
initializer."
I am not sure entirely what this covers, but I think it means that we can now use the same idiom for floating point and possibly string literals.

Why enum instead of static bool?

Why is it considered better practice to use enum not static const bool in template metaprogramming?
I've read that somewhere in Alexandrescu's book but cannot find it and really would like to know it.
The key reason is that a static bool is a variable after all and a enum is a type - hence in case of enum no variable is ever instantiated and it's thus guaranteed to be a compile-time evaluation.
Also see this question for more details.
The C++ standard (ISO/IEC 14882:2003) permits the usage of a static const bool only where an integral constant-expression is required.
In pre-standard C++, all static data members (including const members) required a definition outside of their class. However, during the C++ standardization process it was decided to lift this requirement for static const integral members. The intent was to allow uses such as:
struct C
{
static const int N = 10;
};
char data[C::N]; // N "used" without out-of-class definition
without a namespace scope definition for N.
Nevertheless, the wording of the 1998 C++ standard still required a definition if the member was used in the program. This included the member appearing anywhere except as the operand to sizeof or typeid, effectively making the above ill-formed.
This was identified as a defect, and the wording was adjusted to allow such a member to appear anywhere a constant expression is required, without requiring an out-of-class definition. This includes array bounds, case expressions, static member initializers, and nontype template arguments.
struct C
{
static const int N = 10;
static const int U = N; // Legal per C++03
};
char data[C::N]; // Legal per C++03
template<int> struct D;
template<> struct D<C::N> {}; // Legal per C++03
However, using a static const integral member anywhere except where an integral constant-expression is required requires a definition. But most compilers won't diagnose this violation:
struct C
{
static const int N = 10;
};
int main()
{
int i = C::N; // ill-formed, definition of C::N required
}
This pitfall, however, does not apply to enums.
That's an old recommendation based on old compilers' deficiencies related to inline initialization of static const primitives inside of class definitions. static bool const is the overwhelmingly usual approach at this point.
From the C++03 standard, ยง9.4.2/4:
If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.