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

青春壹個敷衍的年華 提交于 2019-12-01 15:35:44
Columbo

This is core issue 728, which was filed before generic lambdas were a thing.

You mentioned generic lambdas and that they were identical to local classes with corresponding member template operator(). However, they actually aren't, and the differences are related to implementation characteristics. Consider

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

And

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

Instantiating these templates with <void> will be fine in the first case, but ill-formed in the second. Why fine in the first case? foo need not be instantiatable for each particular T, but just one of them (this would be [temp.res]/(8.1)).

Why ill-formed in the second case? The generic lambda's body is instantiated - partially - using the provided template arguments. And the reason for this partial instantiation is the fact that…

…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) We must instantiate enough of the local "template" to make it independent of the local context (which includes template parameters of the enclosing function template).

This is also related to the rationale for [expr.prim.lambda]/13, which mandates that an entity is implicitly captured by a lambda if it…

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.

That is, if I have a lambda like [=] (auto x) {return (typename decltype(x)::type)a;}, where a is some block-scope variable from an enclosing function, regardless of whether x's member typedef is for void or not, the cast will cause a capture of a, because we must decide on this without waiting for an invocation of the lambda. For a discussion of this problem, see the original proposal on generic lambdas.

The bottom line is that completely postponing instantiation of a member template is not compatible with the model used by (at least one) major implementation(s), and since those are the expected semantics, the feature was not introduced.


Was that the original motivation for this constraint? It was introduced sometime between January and May 1994, with no paper covering it, so we can only get a rough idea of the prevailing notions from this paper's justification of why local classes shall not be template arguments:

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

Perhaps back then, one wanted to KISS.

I assume the problem lays more in c++ standard

Correct. This is stipulated in [temp] for class templates:

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

and [temp.mem] for member templates:

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?

Because once we had lambdas in C++11, it was deemed that it would be extremely useful to extend that concept to have generic lambdas. There was a proposal for such a language extension, which was revised and revised and adopted.

On the other hand, there has not yet been a proposal presented (as far as I'm aware from a brief search) that lays out the motivation for the need for member templates in local classes that isn't adequately solved by a generic lambda.

If you feel that this is an important problem that needs to be solved, feel free to submit a proposal after laying out a thoughtful motivation for why local member templates are important.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!