为什么允许使用通用 lambda 而不允许使用模板化方法的嵌套结构?

Why generic lambdas are allowed while nested structs with templated methods aren't?

据我了解 - 通用 lambda 被转换为具有模板化 operator() 的局部作用域结构的对象。这使得通用 lambda 成为非常强大且易于使用的工具。另一方面,可以创建嵌套到函数中的结构,但是当结构具有模板化成员时,例如:

#include <iostream>

int main() {
    struct inner {
    template <class T>
       void operator()(T &&i) { }
    };
    return 0;
}

或自己模板化:

int main() {
    template <class T>
    struct inner {
       void operator()(T &&i) { }
    };
    return 0;
}

编译器好像编译有问题:

error: invalid declaration of member template in local class

and

error: a template declaration cannot appear at block scope

我认为问题出在 C++ 标准中,而不是编译器错误中。允许 lambda 具有模板化成员而不是本地结构的原因是什么?

我找到了 this qustion,但我认为答案有点过时(我认为即使对于 c++11 也是如此)。

I assume the problem lays more in c++ standard

正确。 class 模板的 [temp] 中规定:

A template-declaration can appear only as a namespace scope or class scope declaration.

和 [temp.mem] 成员模板:

A local class of non-closure type shall not have member templates.


What are the reasons lambdas are allowed to have templated members and not the local structures?

因为一旦我们在 C++11 中有了 lambda,人们认为将该概念扩展为具有通用 lambda 将非常有用。有一个proposal for such a language extension, which was revised and revised,被采纳了。

另一方面,目前还没有提出一项提案(据我通过简短搜索得知)阐明在本地 classes 中需要成员模板的动机通用 lambda 不能充分解决这个问题。

如果您觉得这是一个需要解决的重要问题,请在对本地成员模板的重要性提出深思熟虑的动机后,随时 submit a proposal

这是 core issue 728,它是在通用 lambda 出现之前提交的。

您提到了通用 lambda,它们与具有相应成员 template operator() 的本地 类 相同。然而,它们实际上不是,差异与实现特性有关。考虑

template <typename T>
class X {
    template <typename>
    void foo() {
        T t;
    }
};

template <typename T>
auto bar() {
    return [] (auto) {T t;};
};

<void> 实例化这些模板在第一种情况下没问题,但在第二种情况下格式不正确。为什么在第一种情况下很好? foo 不需要为每个特定的 T 实例化,而只是其中之一(这将是 [temp.res]/(8.1))。

为什么第二种情况是病式的?通用 lambda 的主体是部分实例化的,使用提供的模板参数。这种部分实例化的原因是......

…the lexical scopes used while processing a function definition are fundamentally transient, which means that delaying instantiation of some portion of a function template definition is hard to support.

(Richard Smith) 我们必须实例化足够多的本地 "template" 以使其独立于本地上下文(包括封闭函数模板的模板参数)。

这也与理由有关 [expr.prim.lambda]/13,它要求实体被 lambda 隐式捕获,如果它......

names the entity in a potentially-evaluated expression ([basic.def.odr]) where the enclosing full-expression depends on a generic lambda parameter declared within the reaching scope of the lambda-expression.

也就是说,如果我有一个像 [=] (auto x) {return (typename decltype(x)::type)a;} 这样的 lambda,其中 a 是来自封闭函数的一些块作用域变量,而不管 x 的成员 typedef 是对于 void 与否,转换将导致捕获 a,因为我们必须在不等待 lambda 调用的情况下决定这一点。有关此问题的讨论,请参阅 original proposal on generic lambdas.

最重要的是,完全推迟成员模板的实例化与(至少一个)主要实现所使用的模型不兼容,并且由于这些是预期的语义,因此未引入该功能。


这是这个限制的最初动机吗?它是在 1994 年 1 月到 5 月之间的某个时候引入的,没有论文涵盖它,所以我们只能从 this paper 对为什么局部 类 不应该是模板参数的理由中大致了解流行的概念:

Class templates and the classes generated from the template are global scope entities and cannot refer to local scope entities.

也许那时候,有人想亲一口。