La lambda sans capture est-elle garantie d'être vide selon la norme?

12

Je cherche un moyen d'identifier les lambdas vides (sans capture) des autres lambdas dans une fonction de modèle. J'utilise actuellement C ++ 17 mais je suis également curieux de connaître les réponses C ++ 20.

Mon code ressemble à ceci:

template<typename T>
auto func(T lambda) {
    // The aguments of the lambdas are unknown

    if constexpr (/* is captureless */) {
        // do stuff
    }
}

Est-il garanti par la norme C ++ (17 ou 20) qu'un lambda sans capture, qui est convertible en pointeur de fonction, rendra également le std::is_emptyrendement vrai?

Prenez ce code comme exemple:

auto a = []{}; // captureless
auto b = [c = 'z']{}; // has captures

static_assert(sizeof(a) == sizeof(b)); // Both are the same size
static_assert(!std::is_empty_v<decltype(b)>); // It has a `c` member
static_assert(std::is_empty_v<decltype(a)>); // Passes. It is guaranteed?

Exemple en direct

Guillaume Racicot
la source
2
Si vous ne vous souciez que des lambdas non modèles, vous pouvez utiliser SFINAE pour vérifier si la conversion en pointeur de fonction ( +lambda) est bien formée.
HolyBlackCat
@HolyBlackCat J'y ai pensé, mais pour autant que je m'en souvienne, MSVC ne permet pas cela car ils ont surchargé l'opérateur de conversion.
Guillaume Racicot
@GuillaumeRacicot MS expose un opérateur de conversion distinct pour toutes les conventions d'appel disponibles. Choisissez-en un et essayez de convertir le lambda en un pointeur de fonction comparable, et vérifiez si cela réussit ou échoue.
Remy Lebeau
+semble fonctionner ici .
HolyBlackCat

Réponses:

13

Non, en fait, la norme accorde explicitement la permission aux lambdas d'avoir une taille qui ne correspond pas à leur déclaration. [expr.prim.lambda.closure] / 2 états

Le type de fermeture est déclaré dans la plus petite étendue de bloc, portée de classe ou étendue d'espace de noms qui contient l'expression lambda correspondante. [Remarque: Ceci détermine l'ensemble des espaces de noms et des classes associés au type de fermeture ([basic.lookup.argdep]). Les types de paramètres d'un lambda-declarator n'affectent pas ces espaces de noms et classes associés. - note de fin] Le type de fermeture n'est pas un type agrégé. Une implémentation peut définir le type de fermeture différemment de ce qui est décrit ci-dessous à condition que cela ne modifie pas le comportement observable du programme autrement qu'en changeant:

  • la taille et / ou l'alignement du type de fermeture,

  • si le type de fermeture est facilement copiable ([class.prop]), ou (2.3)

  • si le type de fermeture est une classe à disposition standard ([class.prop]).

Une implémentation ne doit pas ajouter de membres du type de référence rvalue au type de fermeture.

mettre l'accent

Cela permet donc à l'implémentation de donner un membre lambda même s'il est sans capture. Je ne pense pas qu'une mise en œuvre le ferait jamais, mais ils sont légalement autorisés à le faire.

NathanOliver
la source