Question Pourquoi les lambdas génériques sont-ils autorisés alors que les structures imbriquées avec des méthodes basées sur des modèles ne le sont pas?


Si je comprends bien - les lambdas génériques sont transformés en objets de structures locales avec des modèles operator(). Cela rend l'outil générique lambda très puissant et facile à utiliser. D'autre part, on peut créer des structures imbriquées dans la fonction, cependant, la structure a un membre basé sur un modèle, par exemple:

#include <iostream>

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

ou est templated par lui-même:

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

le compilateur semble avoir du mal à le compiler:

error: invalid declaration of member template in local class

et

error: a template declaration cannot appear at block scope

Je suppose que le problème réside davantage dans le standard c ++ que dans le bogue du compilateur. Quelles sont les raisons pour lesquelles les lambdas sont autorisés à avoir des membres modèles et non les structures locales?

j'ai trouvé cette question, mais je pense que la réponse est un peu dépassée (je ne pense pas que ce soit vrai même pour c ++ 11).


32
2017-10-25 14:19


origine


Réponses:


C'est question principale 728, qui a été déposé avant que les génériques ne soient une chose.

Vous avez mentionné les lambdas génériques et qu'ils étaient identiques aux classes locales avec le membre correspondant template operator(). Cependant, ils ne le sont pas réellement et les différences sont liées aux caractéristiques de la mise en œuvre. Considérer

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

Et

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

Instancier ces modèles avec <void> sera bien dans le premier cas, mais mal formé dans le second. Pourquoi bien dans le premier cas? foo pas besoin d'être instantiatable pour chaque particulier T, mais juste l'un d'entre eux (ce serait [temp.res] / (8.1)).

Pourquoi mal formé dans le second cas? Le corps générique de lambda est instancié - partiellement - à l'aide des arguments fournis. Et la raison de cette instanciation partielle est le fait que ...

... les portées lexicales utilisées lors du traitement d'une définition de fonction sont fondamentalement transitoires, ce qui signifie que retarder l'instanciation d'une partie d'une définition de modèle de fonction est difficile à prendre en charge.

(Richard Smith) Nous devons instancier suffisamment le "modèle" local pour le rendre indépendant du contexte local (qui inclut les paramètres de modèle du modèle de fonction englobant).

Ceci est également lié à la justification de [expr.prim.lambda] / 13, qui oblige une entité à être implicitement capturée par un lambda si elle ...

nomme l'entité dans une expression potentiellement évaluée ([basic.def.odr]) où la pleine expression dépend d'un paramètre lambda générique déclaré dans la portée de la expression lambda.

C'est-à-dire si j'ai un lambda comme [=] (auto x) {return (typename decltype(x)::type)a;}, où a est une variable bloc-étendue d'une fonction englobante, qu'elle soit ou non xle membre de typedef est pour void ou non, le casting provoquera une capture de a, parce que nous devons décider de cela sans attendre une invocation de la lambda. Pour une discussion de ce problème, voir le proposition originale sur les lambdas génériques.

L'essentiel est que le report complet de l'instanciation d'un modèle membre n'est pas compatible avec le modèle utilisé par (au moins une) implémentation (s) majeure (s), et que celles-ci constituent la sémantique attendue.


Était-ce la motivation originale de cette contrainte? Il a été introduit entre janvier et mai 1994, sans papier pour le couvrir. Nous ne pouvons donc avoir qu'une idée approximative des notions dominantes ce papierLa justification de la raison pour laquelle les classes locales ne doivent pas être des arguments de modèle:

Les modèles de classe et les classes générées à partir du modèle ont une portée globale   entités et ne peuvent pas faire référence à des entités de portée locale.

Peut-être à l'époque, on voulait baiser.


27
2017-10-25 15:28



Je suppose que le problème réside davantage dans le standard c ++

Correct. Ceci est stipulé dans [temp] pour les modèles de classe:

UNE déclaration de modèle ne peut apparaître qu'en tant que déclaration d'espace d'étendue ou de portée de classe.

et [temp.mem] pour les modèles de membres:

Une classe locale de type non-fermeture ne doit pas avoir de modèle de membre.


Quelles sont les raisons pour lesquelles les lambdas sont autorisés à avoir des membres modèles et non les structures locales?

Une fois que nous avons eu lambdas en C ++ 11, il a été considéré qu'il serait extrêmement utile d'étendre ce concept à des lambdas génériques. Il y avait un proposition pour une telle extension de langue, qui était modifié  et révisé et adopté.

D'un autre côté, il n'y a pas encore eu de proposition présentée (pour autant que je sache à partir d'une brève recherche) qui expose la motivation pour le besoin de modèles de membres dans des classes locales qui ne sont pas correctement résolus par un lambda générique. .

Si vous estimez que ce problème important doit être résolu, n'hésitez pas à le faire. soumettre une proposition après avoir établi une motivation réfléchie pour savoir pourquoi les modèles de membres locaux sont importants.


3
2017-10-25 15:23