1

So I've got this code (compiling with clang x86-64 14.0.0):

#include <cstdlib>

template <int size>
struct container {
    char data[size];
};

template <size_t n>
consteval size_t func2(const char (&string)[n]) {
    return 1;
}

template <size_t n>
consteval auto func1(const char (&string)[n]) {
    container<func2(string)> result { };
    return result;
}

int main() {
    static constexpr auto var = func1("hi");
}

The issue comes into play at the func2(string) function call. Replacing string with any string literal yields a successful compilation, whereas leaving string as-is causes a compiler error:

non-type template argument is not a constant expression

I fail to see why this non-type template parameter is not a constant expression, does someone have an explanation?

Nik Tedig
  • 453
  • 2
  • 6
  • 3
    function parameters are never constant expressions. – NathanOliver Sep 26 '22 at 18:18
  • @NathanOliver: While that's true, `consteval` functions have certain interactions with the parameters of `consteval` functions. – Nicol Bolas Sep 26 '22 at 18:25
  • @NathanOliver That seems intensely unreasonable in the context of consteval functions. Are there any plans to remove this restriction? – Nik Tedig Sep 26 '22 at 18:29
  • 1
    @NikTedig Not that I am aware of. Here is some good reading about this: https://stackoverflow.com/questions/56130792/will-consteval-functions-allow-template-parameters-dependent-on-function-argumen – NathanOliver Sep 26 '22 at 18:34
  • @NathanOliver Thx, the link answered my question. – Nik Tedig Sep 26 '22 at 18:39
  • @NicolBolas Do you have a reference for that? Everything I recall seeing says the function parameters behave the same way they do in constexpr functions. – NathanOliver Sep 26 '22 at 18:39
  • @NikTedig No problem. Do you want me to close this as a duplicate of that post? – NathanOliver Sep 26 '22 at 18:41
  • @NathanOliver Seems like a good idea, go ahead. I'm assuming I can't do it myself, since I don't see a button for that. – Nik Tedig Sep 26 '22 at 18:45
  • 1
    @NathanOliver The special behavior is that a call to a `consteval` function inside another `consteval` function doesn't require the nested `consteval` function call to be a constant expression by itself as well. But I don't think there is anything special if the call is part of an expression that is required to be a constant expression. – user17732522 Sep 26 '22 at 18:59

1 Answers1

0

That the function is consteval is not relevant. The expression in the template argument must by itself be a constant expression, which it is not because string is a reference-type function parameter (and so its lifetime didn't start with the evaluation of this constant expression) and can't be named to in a constant expression.

If it wasn't required that the expression is by itself a constant expression regardless of whether it is located in a consteval function or called as part of a constant expression, then you would be able to change the type of an expression as a function of the value passed as function argument. That is not compatible with the type system. (What would e.g. the result of decltype/std::declval on a call to such a function be?)

However, your functions don't actually try to retrieve the value or address of the object that string references at all. So it seems like that shouldn't be an issue and it doesn't have to be.

Up to C++20 there is simply a somewhat unnecessary restriction on constant expressions which disallow naming a reference which is not usable in constant expressions, even if no value or address of the referenced object is accessed. With a non-reference parameter on func1 it would work (but then built-in arrays don't support that).

The restriction will be lifted in C++23 through P2280, apparently also as a defect report against previous C++ revisions according to the poll results mentioned in the document. The paper mentions basically the same example of obtaining the size of an array. Again, consteval is not relevant to this though.

user17732522
  • 53,019
  • 2
  • 56
  • 105