Will consteval functions allow template parameters dependent on function arguments?
Asked Answered
C

3

95

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;
}
Cleasta answered 14/5, 2019 at 12:43 Comment(3)
AFAIK, no. i is (yet) not considered a core constant expression at that very moment.Marjoram
cppreference has The consteval specifier declares a function or function template to be an immediate function [...] An immediate function is a constexpr function, and must satisfy the requirements applicable to constexpr functions or constexpr constructors, as the case may be. so signs point to no. My reading of the standard leads me to the same conclusion but I'm not certain enough to state that in an answer.Beggarly
This question is being discussed on meta.Mosaic
M
51

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.

Moussaka answered 14/5, 2019 at 13:16 Comment(2)
Note that it does allow you to pass an argument of a consteval function to another consteval function, even though the argument is not technically a constant expression.Austronesia
@NicolBolas That's by permission on the side of immediate functions though, not on the side of the arguments. As you said: those aren't constant expressions, and template parameters require those for good reason.Moussaka
P
40

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

Pattani answered 14/5, 2019 at 13:12 Comment(2)
Can't we use a helper like consteval int to_constexpr(int x) { return x; }, and then return std::integral_constant<int, to_constexpr(i)>::value; from (the non-templated version of) foo()? If yes, then current non-constexpr'ness of parameters looks like a useless restriction.Knapsack
@Knapsack No. Because to_constexpr(x) is not a constant expression, which is allowed as it's inside a consteval function definition.Moussaka
C
11

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…

Carmon answered 14/5, 2019 at 13:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.