在C ++ 17中,此代码是非法的:
python manage.py shell
这是因为即使constexpr int foo(int i) {
return std::integral_constant<int, i>::value;
}
可以在编译时求值,编译器仍然需要产生指令以在运行时执行它,从而使模板实例化成为不可能。
在C ++ 20中,我们将拥有foo
函数,这些函数需要在编译时进行评估,因此应删除运行时约束。这是否意味着该代码是合法的?
consteval
答案 0 :(得分:31)
不。
无论纸张将发生什么变化(at this point很少),它都不能改变非二倍体函数定义只键入一次的事实。此外,如果您建议的代码合法,那么我们大概可以找到一种声明类型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.
简而言之,由于可能存在键入差异,因此函数参数永远不会是常量表达式。
答案 1 :(得分:23)
这是否意味着该代码是合法的?
consteval int foo(int i) { return std::integral_constant<int, i>::value; }
不。这仍然是错误的形式。尽管consteval
要求调用本身是一个常量表达式,但是您知道产生i
的参数必须是一个常量表达式,而foo
本身仍然不是模板。模板?
您的示例中的一些细微变化可能会更加明显:
consteval auto foo(int i) {
return std::integral_constant<int, i>();
}
如果这是有效的,foo(1)
和foo(2)
将...返回不同的类型。这是一种完全不同的语言功能(constexpr function parameters)-因为要使其正常工作,此类功能实际上必须像模板一样工作。
这似乎有点不直观。毕竟,如果产生i
的参数是一个常量表达式,那么i
当然也应该可用吗?但这还不是-[expr.const]中没有其他允许立即函数使用参数的异常。即时函数仍然只是一个函数,其参数仍然不是常数表达式,就像普通constexpr
函数的参数不是常数表达式一样。
当然,对于int
,我们可以重写函数以将函数参数提升为模板参数:
template <int i>
consteval int foo() {
return std::integral_constant<int, i>::value;
}
C ++ 20将类类型作为非类型模板参数提供给我们,因此我们实际上可以比以前拥有更多类型。但是,仍有许多类型可以用作无法用作模板参数的立即函数的参数-因此,这种类型并不总是有效(例如std::optional
,或者在C ++ 20中更令人兴奋,std::string
)。
答案 2 :(得分:7)
在C ++ 20中,这似乎不合法。 @Barry和@Columbo的答案已经很好地解释了为什么这会给支持带来问题(它实际上不适用于类型系统)。我将在此处添加我认为是该标准中相关引用的内容,这些内容实际上使该操作非法。
非类型 template-parameter 的 template-argument 应该是转换后的常量表达式[…]
转换后的常数表达式是一个隐式转换为特定类型[expr.const]/7(此处为模板参数的类型)的常数表达式。因此,您的问题可以归结为约束函数中的变量是否为常量表达式的问题。基于[expr.const]/8
常量表达式可以是glvalue核心常量表达式,指的是作为常量表达式允许的结果的实体(定义如下),或者是prvalue核心常量表达式,其值满足以下约束:[…] < / p>
表达式i
是一个glvalue id-expression ,它是一个核心常量表达式(因为它的评估不执行[expr.const]/4中列出的任何事情)。但是,此核心常量表达式所引用的实体不是常量表达式[expr.const]/8的允许结果:
一个实体是一个常量表达式的允许结果,如果它是一个具有静态存储持续时间的对象,该对象不是临时对象,或者是其值满足上述约束的临时对象,或者这是一个非立即函数。
有问题的对象既不是静态存储期限,也不是临时对象……