consteval 函数是否允许模板参数依赖于函数参数?

Will consteval functions allow template parameters dependent on function arguments?

在 C++17 中,这段代码是非法的:

constexpr int foo(int i) {
    return std::integral_constant<int, i>::value;
}

那是因为即使foo可以在编译时求值,编译器仍然需要生成指令在运行时执行它,从而使模板实例化成为不可能。

在 C++20 中,我们将有 consteval 函数,这些函数需要在编译时求值,因此应删除运行时约束。这是否意味着此代码将合法?

consteval int foo(int i) {
    return std::integral_constant<int, i>::value;
}

Does it mean this code will be legal?

consteval int foo(int i) {
    return std::integral_constant<int, i>::value;
}

没有。这还是ill-formed。虽然 consteval 要求调用本身是一个常量表达式,所以你知道产生 i 的参数必须是一个常量表达式,但 foo 本身仍然不是模板。模板?

您的示例中的细微变化可能会使这一点更加明显:

consteval auto foo(int i) {
    return std::integral_constant<int, i>();
}

如果这是有效的,foo(1)foo(2) 将... return 不同的类型。这是一个完全不同的语言特性 (constexpr function parameters) - 因为为了让它工作,这些函数确实需要表现得像模板。

这似乎有点不直观。毕竟,如果生成 i 的参数是一个常量表达式,那么 i 肯定也可以用作一个吗?但它仍然不是 - [expr.const] 中没有额外的例外允许立即函数的参数。即时函数仍然只是一个函数,它的参数仍然不是常量表达式——就像普通 constexpr 函数的参数不是常量表达式一样。


当然有了int,我们可以重写函数,将函数参数提升为模板参数:

template <int i>
consteval int foo() {
    return std::integral_constant<int, i>::value;
}

并且 C++20 为我们提供了 class 类型作为 non-type 模板参数,因此我们实际上可以为比以前更多的类型执行此操作。但是仍然有很多类型可以用作我们不能用作模板参数的立即函数的参数——所以这并不总是有效(例如 std::optional 或者,更令人兴奋的是在 C++20 中, std::string).

没有。

无论这篇论文有什么变化,都是很小的 at this point,它不能改变 non-template 函数定义只输入一次的事实。此外,如果您提议的代码是合法的,我们大概可以找到一种方法来声明 std::integral_constant<int, i> 类型的变量,这在 ODR 方面感觉非常令人望而却步。

本文还指出,在其中一个示例中,参数不打算被视为核心常量表达式;

consteval int sqrsqr(int n) {
  return sqr(sqr(n)); // Not a constant-expression at this  point,
}                     // but that's okay.

简而言之,由于可能存在类型差异,函数参数永远不会是常量表达式。

这在 C++20 中似乎是不合法的。 @Barry 和@Columbo 的回答中已经给出了为什么这对支持有问题的一个很好的解释(它实际上不适用于类型系统)。我将在此处添加我认为是标准的相关引用,这些引用实际上是非法的。

基于[temp.arg.nontype]/2

A template-argument for a non-type template-parameter shall be a converted constant expression […]

转换后的常量表达式是隐式转换为特定类型的常量表达式[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: […]

表达式i是一个glvalueid-expression,它是一个核心常量表达式(因为它的计算不做[中列出的任何事情) =16=]:

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.

有问题的对象既不是静态存储期限也不是临时对象...