According to this, a function declared with constexpr must satisfy a few requirements, one of which is as follows:
there exists at least one argument value such that an invocation of the function could be an evaluated subexpression of a core constant expression ...
Well, constexpr function can have no arguments:
constexpr int Bar( /* empty */ ) { return 0xFF; }
constexpr int value = Bar(); // Valid expression
constexpr function that is invoked as a sub-routine can not determine the whole expression to be core constant expression either.
So what does it mean by one argument value must exist?
[Update for future readers]
Apparently the description about the requirements of constexpr function has been fixed since this question from:
there exists at least one argument value such that an invocation of the function could be an evaluated subexpression of a core constant expression ...
to:
there exists at least one set of argument values such that an invocation of the function could be an evaluated subexpression of a core constant expression ...
The quote from en.cppreference.com is not accurate in regards to the standard, the real quote is (§7.1.5/5):
For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.20) [...] the program is ill-formed; no diagnostic required.
Which basically says that there must exist one valid set of arguments (the empty set being a valid one in your case).
"One argument value" here means "one set of arguments". Nullary functions have one possible argument set, the empty set.
In your case, this single empty argument set does lead to a valid constexpr call, so everything is fine.
Related
In C++17, this code is illegal:
constexpr int foo(int i) {
return std::integral_constant<int, i>::value;
}
That's because even if foo can be evaluated at compile-time, the compiler still needs to produce the instructions to execute it at runtime, thus making the template instantiation impossible.
In C++20 we will have consteval functions, which are required to be evaluated at compile-time, so the runtime constraint should be removed. Does it mean this code will be legal?
consteval int foo(int i) {
return std::integral_constant<int, i>::value;
}
No.
Whatever changes the paper will entail, which is little at this point, it cannot change the fact that a non-template function definition is only typed once. Moreover, if your proposed code would be legal, we could presumably find a way to declare a variable of type std::integral_constant<int, i>, which feels very prohibitive in terms of the ODR.
The paper also indicates that parameters are not intended to be treated as core constant expressions in one of its examples;
consteval int sqrsqr(int n) {
return sqr(sqr(n)); // Not a constant-expression at this point,
} // but that's okay.
In short, function parameters will never be constant expressions, due to possible typing discrepancy.
Does it mean this code will be legal?
consteval int foo(int i) {
return std::integral_constant<int, i>::value;
}
No. This is still ill-formed. While consteval requires the call itself to be a constant expression, so you know that the argument that produces i must be a constant expression, foo itself is still not a template. Template?
A slight variation in your example might make this more obvious:
consteval auto foo(int i) {
return std::integral_constant<int, i>();
}
Were this to be valid, foo(1) and foo(2) would... return different types. This is an entirely different language feature (constexpr function parameters) - because in order for this to work, such functions would really need to behave like templates.
It may seem a little unintuitive. After all, if the argument that produced i was a constant expression, surely i should be usable as one as well? But it's still not - there are no additional exceptions in [expr.const] that permit parameters for immediate functions. An immediate function is still just a function, and its parameters still aren't constant expressions -- in the same way that a normal constexpr function's parameters aren't constant expressions.
Of course with int, we can just rewrite the function to lift the function parameter into a template parameter:
template <int i>
consteval int foo() {
return std::integral_constant<int, i>::value;
}
And C++20 gives us class types as non-type template parameters, so we can actually do this for many more types than we could before. But there are still plenty of types that we could use as a parameter to an immediate function that we cannot use as a template parameter - so this won't always work (e.g. std::optional or, more excitingly in C++20, std::string).
It would seem that this will not be legal in C++20. A good explanation for why this would be problematic to support has already been given in the answers by #Barry and #Columbo (it doesn't really work with the type system). I'll just add what I believe to be the relevant quotes from the standard here that actually make this illegal.
Based on [temp.arg.nontype]/2
A template-argument for a non-type template-parameter shall be a converted constant expression […]
A converted constant expression is a constant expression that is implicitly converted to a particular type [expr.const]/7 (here, the type of the template parameter). So your question boils down to the question of whether a variable inside a consteval function is a constant expression. Based on [expr.const]/8
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints: […]
The expression i is a glvalue id-expression that is a core constant expression (because its evaluation does not do any of the things listed in [expr.const]/4). However, the entity this core constant expression refers to is not a permitted result of a constant expression [expr.const]/8:
An entity is a permitted result of a constant expression if it is an object with static storage duration that either is not a temporary object or is a temporary object whose value satisfies the above constraints, or if it is a non-immediate function.
The object in question is neither of static storage duration nor is it a temporary object…
Consider the following program:
template<typename T>
constexpr int f()
{
T{}.i; // error if instantiated with [T = double]
return 42;
}
constexpr void g(char);
using U = decltype( g( {f<double>()} ) );
To my understanding, the last line is an error because the call to f<double>() is within a brace initializer, and even though f<T> returns an int, the value of the returned int is needed to decide if it can be narrowed to a char as expected by g. This requires the definition of f to be instantiated with double, which causes an error. Both gcc and clang reject this code.
However, if the definition of g is changed to accept an int parameter:
constexpr void g(int);
then it seems that there is no need to instantiate the definition of f, since the narrowing conversion must succeed. Indeed, gcc accepts this, but clang still instantiates f with double and rejects the code. Additionally, if f is only declared, but not defined, clang accepts the code, which implies that the definition is not needed, and shouldn't be instantiated.
Is my reasoning correct, and this is a clang bug, or is instantiation required, and this is actually a gcc bug?
This is CWG #1581 I think, resolved by P0859.
temp.inst/5 says:
Unless a function template specialization is a declared specialization, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist or if the existence of the definition affects the semantics of the program.
Does the existence affect the semantics of the program?
temp.inst/8 says:
The existence of a definition of a variable or function is considered to affect the semantics of the program if the variable or function is needed for constant evaluation by an expression ([expr.const]), even if constant evaluation of the expression is not required or if constant expression evaluation does not use the definition.
Is it needed for constant evaluation by an expression?
expr.const/15.6-7 says:
A function or variable is needed for constant evaluation if it is:
a constexpr function that is named by an expression that is potentially constant evaluated, or
a variable whose name appears as a potentially constant evaluated expression that is either a constexpr variable or is of non-volatile const-qualified integral type or of reference type.
Is it named by an expression that is potentially constant evaluated?
expr.const/15.1-15.5 says:
An expression or conversion is potentially constant evaluated if it is:
a manifestly constant-evaluated expression,
a potentially-evaluated expression,
an immediate subexpression of a braced-init-list,
an expression of the form & cast-expression that occurs within a templated entity, or
a subexpression of one of the above that is not a subexpression of a nested unevaluated operand.
It is an immediate subexpression of a braced-init-list.
Is the compiler guaranteed to evaluate boolean constexpr expressions that are "tautologies" (e.g. always true or false respectively) in a constexpr environment?
Minimal Example / Clarification
For example, in the following code snipplet (at the line marked with (1)) I invoke a function in a constexpr environment, which I intend to cause a compile time error whenever a non-constexpr function is passed. At least the compiler I use (g++-10.0) does this, even though it could also realize that the expression is always true without evaluating it. The reason I ask this question, is because -- to the best of my knowledge -- in a non-constepxr context, an expression like i >= std::numeric_limits<int>::min() is optimized away to true for an int i .
#include <limits>
constexpr int example_function() { return 1;}
constexpr bool compileTimeErrorDesired = example_function() || true; // (1)
Application Example
If the behavior in (1) is guaranteed, it can for instance be used in a concept, in order to execute different code, depending on whether a function provided as template argument can be evaluated at compile time. I implemented a very short (7 lines-of-code) example which does exactly that here at compiler explorer.
Question
Is the line (1) guaranteed to cause a compile time error if called with a non-constexpr function?
edit Inserted clarification, simplify example due to feedback.
It's guaranteed that f() || true is not a core constant expression if f is not a constexpr function: (constant expressions are more restrictive than core constant expressions) [expr.const]/2.2
An expression e is a core constant expression unless the
evaluation of e, following the rules of the abstract machine,
would evaluate one of the following expressions:
[...]
an invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a
trivial destructor ([class.dtor]) [ Note: Overload resolution
is applied as usual — end note ];
[...]
It's also guaranteed that the program is ill-formed (diagnostic required) if non-constant expressions are used in contexts that require constant expressions. Note that || is defined to evaluate from left to right: [expr.log.or]/1
The || operator groups left-to-right. The operands are both
contextually converted to bool. It returns true if either of its
operands is true, and false otherwise. Unlike |, || guarantees
left-to-right evaluation; moreover, the second operand is not
evaluated if the first operand evaluates to true.
In other words, true || f() is a core constant expression because f() is not evaluated, whereas f() || true is not because f() is evaluated.
Whether or not an expression is a constant expression has nothing to do with optimization — constant expressions are defined based on the rules of the abstract machine.
The terms "executed" isn't exactly appropriate when it comes to constant expressions. Even "evaluated" might be used with care, because whether an expression is a constant expression depends partly on behavior of what would happen if the expression were evaluated, but is not considered an evaluation in the most strict sense.
[expr.const] describes requirements for a number of different contexts of "compile-time behavior", including "constant expression". [expr.const]/(5.2) says that if evaluating an expression would evaluate a non-constexpr function, the expression is not a core constant expression, and therefore not a constant expression. If the expression is used in a context requiring a constant expression (like static_assert, a non-type template argument, etc.), the program is ill-formed and there must be a diagnostic message. There is no rule permitting anything like allowing a non-constant expression in such a context or skipping some parts of the hypothetical evaluation if the expression is a converted constant expression and the result value of the expression can be determined despite not being a constant expression.
So if example_function is not declared constexpr, then example_function() || true is not a constant expression because the evaluation would require calling the function. But true || example_function() is a constant expression, since the evaluation would not call the function.
Your is_constexpr<T> is guaranteed to work, because any semantic constraint violation involving a template parameter inside a requires-expression does not make the program ill-formed, but just makes the requires-expression result value false ([expr.prim.req]/6). In is_constexpr<example_function>, using the non-constant expression T() as a template argument to std::enable_if via the default template argument instantiated by ConstexprHelper<T> is such a semantic error, so the requires-expression has value false.
In C++17, this code is illegal:
constexpr int foo(int i) {
return std::integral_constant<int, i>::value;
}
That's because even if foo can be evaluated at compile-time, the compiler still needs to produce the instructions to execute it at runtime, thus making the template instantiation impossible.
In C++20 we will have consteval functions, which are required to be evaluated at compile-time, so the runtime constraint should be removed. Does it mean this code will be legal?
consteval int foo(int i) {
return std::integral_constant<int, i>::value;
}
No.
Whatever changes the paper will entail, which is little at this point, it cannot change the fact that a non-template function definition is only typed once. Moreover, if your proposed code would be legal, we could presumably find a way to declare a variable of type std::integral_constant<int, i>, which feels very prohibitive in terms of the ODR.
The paper also indicates that parameters are not intended to be treated as core constant expressions in one of its examples;
consteval int sqrsqr(int n) {
return sqr(sqr(n)); // Not a constant-expression at this point,
} // but that's okay.
In short, function parameters will never be constant expressions, due to possible typing discrepancy.
Does it mean this code will be legal?
consteval int foo(int i) {
return std::integral_constant<int, i>::value;
}
No. This is still ill-formed. While consteval requires the call itself to be a constant expression, so you know that the argument that produces i must be a constant expression, foo itself is still not a template. Template?
A slight variation in your example might make this more obvious:
consteval auto foo(int i) {
return std::integral_constant<int, i>();
}
Were this to be valid, foo(1) and foo(2) would... return different types. This is an entirely different language feature (constexpr function parameters) - because in order for this to work, such functions would really need to behave like templates.
It may seem a little unintuitive. After all, if the argument that produced i was a constant expression, surely i should be usable as one as well? But it's still not - there are no additional exceptions in [expr.const] that permit parameters for immediate functions. An immediate function is still just a function, and its parameters still aren't constant expressions -- in the same way that a normal constexpr function's parameters aren't constant expressions.
Of course with int, we can just rewrite the function to lift the function parameter into a template parameter:
template <int i>
consteval int foo() {
return std::integral_constant<int, i>::value;
}
And C++20 gives us class types as non-type template parameters, so we can actually do this for many more types than we could before. But there are still plenty of types that we could use as a parameter to an immediate function that we cannot use as a template parameter - so this won't always work (e.g. std::optional or, more excitingly in C++20, std::string).
It would seem that this will not be legal in C++20. A good explanation for why this would be problematic to support has already been given in the answers by #Barry and #Columbo (it doesn't really work with the type system). I'll just add what I believe to be the relevant quotes from the standard here that actually make this illegal.
Based on [temp.arg.nontype]/2
A template-argument for a non-type template-parameter shall be a converted constant expression […]
A converted constant expression is a constant expression that is implicitly converted to a particular type [expr.const]/7 (here, the type of the template parameter). So your question boils down to the question of whether a variable inside a consteval function is a constant expression. Based on [expr.const]/8
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints: […]
The expression i is a glvalue id-expression that is a core constant expression (because its evaluation does not do any of the things listed in [expr.const]/4). However, the entity this core constant expression refers to is not a permitted result of a constant expression [expr.const]/8:
An entity is a permitted result of a constant expression if it is an object with static storage duration that either is not a temporary object or is a temporary object whose value satisfies the above constraints, or if it is a non-immediate function.
The object in question is neither of static storage duration nor is it a temporary object…
According to this answer apparently there is no good reason why structured bindings are not allowed to be constexpr, yet the standard still forbids it. In this case, however, shouldn't the use of the structured bindings inside the constexpr function also be prohibited? Consider a simple snippet:
#include <utility>
constexpr int foo(std::pair<int, int> p) {
auto [a, b] = p;
return a;
}
int main() {
constexpr int a = foo({1, 2});
static_assert(a == 1);
}
Both gcc and clang does not cause trouble compiling the code. Is the code ill-formed either way or is this one actually allowed?
In the case of function declaration, the constexpr specifier is an assertion made to the compiler that the function being declared may be evaluated in a constant expression, i.e. an expression that can be evaluated at compile-time. Nevertheless the object initialization inside a declaration does not need to have constexpr inside its declaration specifier to be a constant expression.
Shorter: constexpr function may imply constant expression but constant expression initialization does not need that the associated declaration has a constexpr specifier.
You can check this in the C++ standard [dcl.constexpr]:
A call to a constexpr function produces the same result as a call to an equivalent non-constexpr function in
all respects except that
— a call to a constexpr function can appear in a constant expression[...]
This is the evaluation of an expression that determines if an expression is a
constant expression [expr.const]:
An expression e is a core constant expression unless the evaluation of e [...] would evaluate one of the following expression[...]
A declaration is not an expression, so an initialization of an object being declared is a constant expression irrespective of the presence or not of a constexpr specifier in the declaration.
Finally, in [dcl.constexpr], it is specified that a constexpr function must be such that there exist parameters for which its body can be evaluated as a constant expression:
For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument
values exist such that an invocation of the function or constructor could be an evaluated subexpression of
a core constant expression (8.20), or, for a constructor, a constant initializer for some object (6.6.2), the
program is ill-formed, no diagnostic required.
When you declare constexpr int a the compiler expects a to be inialized by a constant expression and the expression foo({1,2}) is a constant expression, so your code is well formed.
PS: Nevertheless, declaration specifiers (static, thread_local=>static) in the the declaration of function local variable implies that the function cannot be declared constexpr.
There are several requirements that a constexpr function must meet. There are some requirements for the body of a constexpr function, and the shown code does not appear to violate any of them. The key point is that there is no requirement that every statement in a function must be a constexpr. The only interesting requirement in question here, is this one:
there exists at least one set of argument values such that an
invocation of the function could be an evaluated subexpression of a
core constant expression (for constructors, use in a constant
initializer is sufficient) (since C++14). No diagnostic is required
for a violation of this bullet.
Note the last sentence. The compiler may, but is not required to, throw a red flag.
The key requirement is merely that there is some assortment of parameter values to the function that results in a constant result from the function (and the function body meets the listed requirements). For example, the function might use a structured binding conditionally; but for some set of parameter values do something else, producing a constant result. This would tick this checkbox for a constexpr function.
But, despite the sophistication of modern C++ compilers, they may not necessarily be capable of reaching this determination in every possible instance, so, in practice, it would be hard to enforce such a requirement, hence the compilers are permitted to just take this for granted.