O lambda sem captura é garantido como vazio pelo padrão?

12

Estou procurando uma maneira de identificar lambdas vazias (sem captura) de outras lambdas em uma função de modelo. Atualmente, estou usando C ++ 17, mas estou curioso para respostas C ++ 20 também.

Meu código fica assim:

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

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

É garantido pelo padrão C ++ (17 ou 20) que um lambda sem captura, que é conversível em um ponteiro de função, também tornará o std::is_emptyrendimento verdadeiro?

Tome este código como um exemplo:

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?

Exemplo ao vivo

Guillaume Racicot
fonte
2
Se você se importa apenas com lambdas sem modelo, você pode usar o SFINAE para verificar se a conversão para um ponteiro de função ( +lambda) está bem formada.
HolyBlackCat 27/01
@HolyBlackCat Pensei nisso, mas até onde me lembro, o MSVC não permite isso, pois sobrecarregou o operador de conversão.
Guillaume Racicot 27/01
O @GuillaumeRacicot MS expõe um operador de conversão separado para todas as convenções de chamada disponíveis. Basta escolher um e tentar converter o lambda em um ponteiro de função comparável e verificar se isso é bem-sucedido ou falha.
Remy Lebeau
+parece funcionar aqui .
HolyBlackCat 27/01

Respostas:

13

Não, de fato, o padrão concede explicitamente permissão para lambdas ter um tamanho que não esteja alinhado com sua declaração. [expr.prim.lambda.closure] / 2 estados

O tipo de fechamento é declarado no menor escopo de bloco, escopo de classe ou espaço de nome que contém a expressão lambda correspondente. [Nota: isso determina o conjunto de espaços para nome e classes associados ao tipo de fechamento ([basic.lookup.argdep]). Os tipos de parâmetro de um declarador lambda não afetam esses espaços de nomes e classes associados. - nota final] O tipo de fechamento não é um tipo agregado. Uma implementação pode definir o tipo de fechamento de maneira diferente do que é descrito abaixo, desde que isso não altere o comportamento observável do programa, exceto alterando:

  • o tamanho e / ou alinhamento do tipo de fechamento,

  • se o tipo de fechamento é trivialmente copiável ([class.prop]) ou (2.3)

  • se o tipo de fechamento é uma classe de layout padrão ([class.prop]).

Uma implementação não deve adicionar membros do tipo de referência rvalue ao tipo de fechamento.

ênfase minha

Portanto, isso permite que a implementação forneça um membro ao lambda, mesmo que seja sem captura. Eu acho que nenhuma implementação seria, mas eles estão legalmente autorizados a fazê-lo.

NathanOliver
fonte