Czy zgodnie z normą gwarantowana lambda bez wychwytywania jest pusta?

12

Poszukuję sposobu na identyfikację pustych (niewychwyconych) lambd z innych lambd w funkcji szablonu. Obecnie używam C ++ 17, ale jestem również ciekawy odpowiedzi na C ++ 20.

Mój kod wygląda następująco:

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

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

Czy standard C ++ (17 lub 20) gwarantuje, że bezbarwna lambda, którą można przekształcić we wskaźnik funkcji, również sprawi, że std::is_emptywydajność będzie prawdziwa?

Weź ten kod jako przykład:

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?

Przykład na żywo

Racilla Guillaume
źródło
2
Jeśli zależy ci tylko na lambdach innych niż szablony, możesz użyć SFINAE, aby sprawdzić, czy konwersja do wskaźnika funkcji ( +lambda) jest poprawnie sformułowana.
HolyBlackCat
@HolyBlackCat Myślałem o tym, ale o ile pamiętam, MSVC nie pozwala na to, ponieważ przeciążają one operator konwersji.
Guillaume Racicot
@GuillaumeRacicot MS udostępnia osobnego operatora konwersji dla wszystkich dostępnych konwencji wywoływania. Wystarczy wybrać jeden i spróbować przekonwertować lambda na porównywalny wskaźnik funkcji i sprawdzić, czy to się powiedzie, czy nie.
Remy Lebeau
+wydaje się, że tutaj działa .
HolyBlackCat

Odpowiedzi:

13

Nie, w rzeczywistości standard wyraźnie zezwala, aby lambdas miały rozmiar niezgodny z ich deklaracją. [expr.prim.lambda.closure] / 2 stany

Typ zamknięcia jest zadeklarowany w najmniejszym zakresie bloku, zakresie klasy lub zakresie przestrzeni nazw zawierającym odpowiednie wyrażenie lambda. [Uwaga: Określa zestaw przestrzeni nazw i klas powiązanych z rodzajem zamknięcia ([basic.lookup.argdep]). Typy parametrów deklaratora lambda nie wpływają na te powiązane przestrzenie nazw i klasy. - uwaga końcowa] Typ zamknięcia nie jest typem zagregowanym. Implementacja może zdefiniować typ zamknięcia inaczej niż opisano poniżej, pod warunkiem że nie zmieni to obserwowalnego zachowania programu inaczej niż poprzez zmianę:

  • rozmiar i / lub wyrównanie rodzaju zamknięcia,

  • czy typ zamknięcia można w prosty sposób skopiować ([class.prop]), czy (2.3)

  • czy typ zamknięcia jest klasą układu standardowego ([class.prop]).

Wdrożenie nie dodaje członków typu odniesienia do wartości do typu zamknięcia.

mój nacisk

Pozwala to więc implementacji na przekazanie lambda członka, nawet jeśli nie jest on przechwytywany. Nie sądzę, aby jakakolwiek implementacja kiedykolwiek, ale są one prawnie dozwolone.

NathanOliver
źródło