约束函数会允许模板参数取决于函数参数吗?

Ann*_*nyo 55 c++ language-lawyer class-template constexpr c++20

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

constexpr int foo(int i) {
    return std::integral_constant<int, i>::value;
}
Run Code Online (Sandbox Code Playgroud)

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

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

consteval int foo(int i) {
    return std::integral_constant<int, i>::value;
}
Run Code Online (Sandbox Code Playgroud)

Col*_*mbo 34

没有。

无论改变纸将需要,这是很少在这一点上,它不能改变的事实,一个非模板函数定义只输入一次。此外,如果您提出的代码合法,那么我们大概可以找到一种方法来声明一个type变量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.

Run Code Online (Sandbox Code Playgroud)

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


Bar*_*rry 25

这是否意味着该代码将是合法的?

consteval int foo(int i) {
    return std::integral_constant<int, i>::value;
}
Run Code Online (Sandbox Code Playgroud)

否。这仍然是错误的形式。尽管consteval要求调用本身是一个常量表达式,所以您知道生成的参数i必须是一个常量表达式,但foo它本身仍不是模板。模板?

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

consteval auto foo(int i) {
    return std::integral_constant<int, i>();
}
Run Code Online (Sandbox Code Playgroud)

这是有效的,foo(1)并且foo(2)将返回不同的类型。这是一种完全不同的语言功能(constexpr函数参数)-因为要使其正常工作,此类功能实际上就需要像模板一样工作。

似乎有点不直观。毕竟,如果产生的参数i是一个常量表达式,那么当然也i应该可用吗?但这还不是-[expr.const]中没有其他允许立即函数使用参数的异常。即时函数仍然只是一个函数,其参数仍然不是常量表达式-就像普通constexpr函数的参数不是常量表达式一样。


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

template <int i>
consteval int foo() {
    return std::integral_constant<int, i>::value;
}
Run Code Online (Sandbox Code Playgroud)

C ++ 20将类类型作为非类型模板参数提供给我们,因此我们实际上可以比以前拥有更多类型。但是,仍有许多类型可以用作无法用作模板参数的立即函数的参数-因此,这种类型并不总是有效的(例如,std::optional或者在C ++ 20中更令人兴奋std::string)。


Mic*_*zel 8

在C ++ 20中,这似乎不合法。@Barry和@Columbo的答案已经很好地解释了为什么这会给支持带来问题(它实际上不适用于类型系统)。我将在此处添加我认为是该标准中相关引用的内容,这些内容实际上使此操作非法。

基于[temp.arg.nontype] / 2

模板参数的用于非类型模板参数应是一个转换后的常量表达式[...]

转换后的常数表达式是隐式转换为特定类型[expr.const] / 7(此处为模板参数的类型)的常数表达式。因此,您的问题可以归结为约束函数中的变量是否为常量表达式的问题。基于[expr.const] / 8

常量表达式可以是glvalue核心常量表达式,指的是作为常量表达式允许的结果(定义如下)的实体,也可以是prvalue核心常量表达式,其值满足以下约束:[…]

该表达式i是glvalue id-expression,它是一个核心常量表达式(因为它的求值没有执行[expr.const] / 4中列出的任何事情)。但是,此核心常量表达式所引用的实体不是常量表达式[expr.const] / 8的允许结果:

如果实体是具有静态存储持续时间的对象,而该对象不是临时对象或者其值满足上述约束的临时对象,或者它是非立即函数,则该实体是常量表达式允许结果

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