【问题标题】:Evaluated constexpr lambda in non-type template argument在非类型模板参数中评估 constexpr lambda
【发布时间】:2017-02-09 02:17:12
【问题描述】:

Lambda 表达式不允许在未计算的上下文中(例如在 decltype 中),并且直到最近才可以是常量表达式。因此无法在模板参数中使用它们。

然而,在 C++17 中,常量表达式 lambdas 是可能的。这仍然不允许在一般模板参数中使用它们。

但是,对于非类型模板参数,常量表达式 lambda 表达式可以在评估上下文中使用,例如:

template<int N> struct S { constexpr static int value = N; };

int main() {
    int N = S<[]()constexpr{return 42;}()>::value;
}

但这仍然行不通,因为无论是类型还是非类型,模板参数中都明确禁止 lambda 表达式。

我的问题是不允许上面的构造背后的原因。我可以理解函数签名中的 lambda 类型可能有问题,但是这里闭包类型本身是无关紧要的,只使用(编译时常量)返回值。

我怀疑原因是 lambda 主体中的所有语句都将成为模板参数表达式的一部分,因此如果主体中的任何语句在替换期间格式不正确,则需要应用 SFINAE。这可能需要编译器开发人员的大量工作。

但这实际上是我的动力。如果可以使用上面的构造,那么 SFINAE 不仅可以用于常量表达式,还可以用于 constexpr 函数中有效的其他语句(例如文字类型声明)。

除了对编译器编写者的影响之外,这是否会导致任何问题,例如标准中的歧义、矛盾或复杂性?

【问题讨论】:

  • 我想你已经回答了你自己的问题。
  • 好吧,+1 @Barry,这实际上是一个有趣的问答。
  • @Barry 我稍微修改了我的问题。我希望得到一些验证,这确实是(唯一的)主要原因,也许是参考委员会文件/讨论中提到的推理。
  • “如果正文中的任何语句在替换期间格式不正确,则需要应用 SFINAE” 我不确定这是否是一个问题 - SFINAE 仅在直接上下文,并且可以想象将 lambda 的主体解释为不是直接的。
  • constexpr auto f=[]()constexpr{return 42;}; 在上一行然后调用 f() 使您的论点没有意义。

标签: c++ templates lambda sfinae c++17


【解决方案1】:

lambda 不会出现在未评估的上下文中是非常有意的。 lambda 总是具有唯一类型的事实导致了各种问题。

以下是来自 Daniel Krugler 的 comp.lang.c++ discussion 的一些示例:

确实存在大量允许 lambda 的用例 表达式,它可能会极大地扩展可能的 sfinae 案例 (包括完整的代码“沙盒”)。他们成为的原因 排除是由于 sfinae 案例的这种极端扩展(你 正在为编译器打开一个潘多拉盒子)以及它可以 导致您的其他示例出现问题,例如

template<typename T, typename U>
void g(T, U, decltype([](T x, T y) { return x + y; }) func);

没用,因为每个 lambda 表达式都会生成唯一的类型,所以 像

g(1, 2, [](int x, int y) { return x + y; });

实际上不起作用,因为在 参数与g调用中的lambda类型不同。

最后,它也确实导致了名称修改问题。例如。当你有

template<typename T>
void f(T, A<sizeof([](T x, T y) { return x + y; })> * = 0);

在一个翻译单元中,但

template<typename T>
void f(T, A<sizeof([](T x, T y) { return x - y; })> * = 0);

在另一个翻译单元中。现在假设您实例化了f&lt;int&gt; 来自两个翻译单位。这两个功能有不同 签名,因此它们必须生成不同的模板 实例化。将它们分开的唯一方法是破坏 body 的 lambda。反过来,这意味着编译器编写者有 为 语言。虽然在技术上是可行的,但这被认为是 规范和实施负担。

这是一大堆问题。尤其是考虑到你写作的动机:

int N = S<[]()constexpr{return 42;}()>::value;

改写可以很容易解决:

constexpr auto f = []() constexpr { return 42; }
int N = S<f()>::value;

【讨论】:

  • 当然,既然我发了这个,我也找到了this Q/A
  • 我不太明白模板参数有什么问题:它们不是签名的一部分,为什么 SFINAE 应该应用于 lambda 主体?
  • @dyp SFINAE 是次要的,首先实际上在那里有 lambda。我认为这不重要。
猜你喜欢
  • 1970-01-01
  • 2016-03-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-02
  • 2020-04-22
  • 2017-09-17
  • 2018-06-17
  • 1970-01-01
相关资源
最近更新 更多