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.
Related
I have a class template for an N-dimensional array:
template<typename T, std::size_t... Shape>
class ndarray { ... };
One consequence of this template design is that there is an additional 'implicit' template parameter if you will: std::size_t Size, the product of all arguments in Shape. I have been using a C++17 fold expression ((1 * ... * Shape)) where I would typically use Size if it weren't dependent on Shape, but I'm wondering if adding the following 'alias' would result in any subtle differences in the compiled assembly:
template<typename T, std::size_t... Shape>
class ndarray {
static constinit std::size_t Size = (1 * ... * Shape);
...
};
Interestingly, the ISO C++20 standard doesn't state whether or not constinit implies inline as it does for constexpr and consteval. I think the semantics of constinit (especially in relation to constexpr variables) only really makes sense if the variable were also inline, but its omission from the standard makes me wary of this conclusion.
constinit has exactly and only one semantic: the expression used to initialize the variable must be a constant expression.
That's it. That's all it does: it causes a compile error if the initializing expression isn't a valid constant expression. In every other way, such a variable is identical to not having constinit there. Indeed, the early versions of the proposal had constinit as an attribute rather than a keyword, since it didn't really do anything. It only gave a compile error for invalid initialization of the variable.
For this case, there is no reason not to make the variable constexpr. You clearly don't intend to change the variable, so there's no point in making the variable modifiable.
No, these are not at all the same because constinit doesn't make the variable const. As such, the initialization of Size isn't even valid. Writing constinit const is mostly equivalent to writing constexpr, which is semantically equivalent to the constant fold-expression. There's no guarantee that any compiler does treat them identically, but in most constant-expression cases there isn't much room for variation.
constinit also doesn't imply inline: it isn't that the standard "doesn't state whether", it's just that there's nothing to say. Since there's no point in putting constinit and constexpr on the same variable, it's not clear what semantics you expect in that area.
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.
This question already has answers here:
What exactly is the "as-if" rule?
(3 answers)
Closed 2 years ago.
Lets have following code in C++14:
using namespace std;
void foo(int a)
{
cout << a;
}
int main()
{
//version1
foo(13);
//version2
static constexpr int tmp = 13;
foo(tmp);
return 0;
}
Will the compiler automatically optimize version2 to version1 so that static constexpr variable will be inlined (something like defines are handled during processor in C)? If so, what are the rules for this? Will it be still inlined if it would be only constexpr or static const ?
From http://eel.is/c++draft/dcl.constexpr#1.sentence-3
A function or static data member declared with the constexpr or consteval specifier is implicitly an inline function or variable.
Compilers are given wide latitude under the "as if" rule to optimize as they see fit. Given the fairly trivial nature of the code you posted, a compiler is just as capable of optimizing "version 2" even if you removed static constexpr from the definition. The compiler can see that you don't change the value of the variable between its initialization and its use, and it can see that the use is a compile-time value, so it can call that function with the initialization value. And even optimize the function itself away if it can see the definition.
Whether any particular compiler will or won't optimize away some code depends on compiler settings and the particular details of the code (specifically, code that intervenes between the value's creation and its use). There are no "rules" for this.
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.
When you attempt to use constexpr with main like this:
constexpr int main()
gcc and clang complain:
error: cannot declare '::main' to be inline
error: 'main' is not allowed to be declared constexpr
Let's see what requirements for constexpr function are:
A constexpr function must satisfy the following requirements:
it must not be virtual
its return type must be LiteralType
each of its parameters must be literal type
What is LiteralType?
A literal type is any of the following
void(since c++14)
scalar type
reference type
an array of literal type
What must the function body include?
null statements
static_assert declarations
typedef declarations and alias declarations that do not define classes or enumerations
using declarations
using directives
exactly one return statement that contains only literal values, constexpr variables and functions.
The following examples:
constexpr int main() { ; }
constexpr int main() { return 42; }
constexpr int main() {
// main defaults to return 0
}
seems to fit all these requirements. Also with that, main is special function that runs at start of program before everything else. You can run constexpr functions from main, and in order for something marked constexpr to be constexpr, it must be run in a constexpr context.
So why is main not allowed to be a constexpr?
No, this is not allowed the draft C++ standard in section 3.6.1 Main function paragraph 3 says:
[...]A program that defines main as deleted or that declares main to be inline, static, or constexpr is ill-formed.[...]
main has to be a run-time function and as Lightness says it makes no sense since you can't optimize main away.
The standard gives the precise signature for main, so the compiler is allowed to reject other signatures. Even more specifically, it prescribes that main cannot be constexpr, static, or some other things.
If you're wondering why, the compiler is allowed to insert code at the beginning of main (to do stuff like initialize global variables, etc.) which could make it non-constexpr (which is why e.g. a program is not allowed to call main explicitly).
It doesn't make any sense to declare main as constexpr for two reasons: 1) It is a run-time function. 2) it may not be called from other functions or recursively.
In my opinion the reason is that it makes no sense to declare main() as a constexpr and the standards committee want the C++ programming language to make sense.
The function main() is a special function that deals with program entry-point initialization - it is not sensible to use it to calculate compile-time values.