c++ - 在非类型模板参数中计算 constexpr lambda

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

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

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

然而,特别是对于非类型模板参数,常量表达式 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 函数中有效的其他语句(例如文字类型声明)。

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

最佳答案

lambda 不出现在未计算的上下文中是有意为之的。 lambda 始终具有唯一类型这一事实会导致各种问题。

这里有几个例子来自 comp.lang.c++ discussion ,来自丹尼尔·克鲁格勒:

There would indeed exist a huge number of use-cases for allowing lambda expressions, it would probably extremely extend possible sfinae cases (to include complete code "sand-boxes"). The reason why they became excluded was due to exactly this extreme extension of sfinae cases (you were opening a Pandora box for the compiler) and the fact that it can lead to problems on other examples as yours, e.g.

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

is useless, because every lambda expression generates a unique type, so something like

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

doesn't actually work, because the type of the lambda used in the parameter is different from the type of the lambda in the call to g.

Finally it did also cause name-mangling issues. E.g. when you have

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

in one translation unit but

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

in another translation unit. Assume now that you instantiate f<int> from both translation units. These two functions have different signatures, so they must produce differently-mangled template instantiations. The only way to keep them separate is to mangle the body of the lambdas. That, in turn, means that compiler writers have to come up with name mangling rules for every kind of statement in the language. While technically possible, this was considered as both a specification and an implementation burden.

这是一大堆问题。特别是考虑到您的写作动机:

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

可以通过以下方式轻松解决:

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

关于c++ - 在非类型模板参数中计算 constexpr lambda,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39792593/

相关文章:

c++ - 仅针对模板中的某些类型覆盖函数

c++ - C++中的类型转换和继承

c# - 没有 else 的简写 If 语句

c++ - 整数类型的 std::swap 有多快?

c++ - 从包含变体数据类型的数据结构中检索值而不指定返回类型

c++ - 需要对返回常量值的方法编写测试吗?

c++ - 为什么我们使用非类型模板参数?

java - 如何在java中使用lambda表达式添加Map

c# - 我可以制作一个将任意表达式作为参数并调用它的实用程序方法吗?

c++ - 父类(super class)需要比初始化列表提供的更多的信息