【问题标题】:Will consteval functions allow template parameters dependent on function arguments?consteval 函数是否允许依赖于函数参数的模板参数?
【发布时间】:2020-04-20 00:10:55
【问题描述】:

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

【问题讨论】:

  • AFAIK,没有。 i 在那一刻(还)不被视为核心常量表达式。
  • cppreference 具有 consteval 说明符将函数或函数模板声明为立即函数 [...] 立即函数是 constexpr 函数,并且必须满足适用于 constexpr 函数或constexpr 构造函数,视情况而定。 所以标志指向否。我对标准的阅读使我得出相同的结论,但我不确定在答案中说明这一点。
  • 这个问题正在meta讨论。

标签: c++ language-lawyer constexpr c++20 class-template


【解决方案1】:

没有。

无论论文会带来什么变化,这只是at this point,它不能改变非模板函数定义只输入一次的事实。此外,如果您提出的代码是合法的,我们大概可以找到一种方法来声明一个std::integral_constant&lt;int, i&gt; 类型的变量,这在 ODR 方面感觉非常令人望而却步。

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

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

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

【讨论】:

  • 请注意,它确实允许您将 consteval 函数的参数传递给另一个 consteval 函数,即使该参数在技术上不是常量表达式。
  • @NicolBolas 不过,这是在立即函数方面的许可,而不是在参数方面。正如您所说:这些不是常量表达式,模板参数需要这些是有充分理由的。
【解决方案2】:

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

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 中,@ 987654335@)。

【讨论】:

  • 我们不能使用consteval int to_constexpr(int x) { return x; } 之类的助手,然后使用foo()(的非模板版本)中的return std::integral_constant&lt;int, to_constexpr(i)&gt;::value; 吗?如果是,那么当前的非constexpr'ness 参数看起来像是一个无用的限制。
  • @Ruslan 否。因为to_constexpr(x) 不是常量表达式,这是允许的,因为它在consteval 函数定义中。
【解决方案3】:

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

基于[temp.arg.nontype]/2

非类型模板参数模板参数应该是转换后的常量表达式[…]

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

一个常量表达式要么是一个泛左值核心常量表达式,它引用一个作为常量表达式(如下定义)的允许结果的实体,要么是一个纯右值核心常量表达式,其值满足以下约束:[…]

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

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

有问题的对象既不是静态存储持续时间,也不是临时对象……

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-24
    • 2012-04-14
    相关资源
    最近更新 更多