By Annyo


2019-05-14 12:43:05 8 Comments

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;
}

3 comments

@Columbo 2019-05-14 13:16:45

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.

@Nicol Bolas 2019-05-14 13:31:22

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.

@Columbo 2019-05-14 13:47:49

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

@Michael Kenzel 2019-05-14 13:18:19

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…

@Barry 2019-05-14 13:12:49

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

@Columbo 2019-05-14 13:28:29

190k? They have a low pressure environment at Jump, yeah? ;-)

@Barry 2019-05-14 13:31:25

@Columbo I've missed you man, somebody's gotta pick up the slack. How's HRT treating you?

@Columbo 2019-05-14 13:46:36

Very well, and I trust the same holds for you. I've been gone for too long—worklife needs getting used to. Hope to be more active going forward! :-) Do you happen to attend WG21 meetings by now?

@Ruslan 2019-05-14 13:48:28

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.

@Columbo 2019-05-14 13:49:24

@Ruslan No. Because to_constexpr(x) is not a constant expression, which is allowed as it's inside a consteval function definition.

@Barry 2019-05-14 15:37:58

@Columbo Yep! Cologne will be my... 7th? You going to make it to one sometime?

Related Questions

Sponsored Content

29 Answered Questions

[SOLVED] enum to string in modern C++11 / C++14 / C++17 and future C++20

16 Answered Questions

[SOLVED] Why can templates only be implemented in the header file?

10 Answered Questions

[SOLVED] Use 'class' or 'typename' for template parameters?

  • 2008-10-17 17:43:59
  • Kristopher Johnson
  • 255189 View
  • 566 Score
  • 10 Answer
  • Tags:   c++ templates

10 Answered Questions

2 Answered Questions

[SOLVED] if vs if constexpr inside constexpr function

1 Answered Questions

[SOLVED] Implementing is_constexpr_copiable

1 Answered Questions

2 Answered Questions

[SOLVED] Size of std::array in class template depending on template parameter

4 Answered Questions

[SOLVED] Is it possible to know when is constexpr really a constexpr?

Sponsored Content