As funções lambda podem ser modeladas?


230

No C ++ 11, existe uma maneira de modelar uma função lambda? Ou é inerentemente específico demais para ser modelado?

Entendo que posso definir uma classe / functor com modelo clássico, mas a pergunta é mais como: a linguagem permite modelar funções lambda?


Existe um caso de uso em que um modelo lambda seria útil?
James McNellis

7
James: Você pode criar uma função para iterar sobre uma tupla (não necessariamente útil).
Joe D

Pensei na idéia enquanto lia uma entrevista do Stroustrup falando sobre a complexidade do meta-modelo ser um problema. Se fosse permitido, eu estava imaginando o ninja código-fu que pode ser inventado por programadores muito inteligentes brincando com estas características combinação ...
Klaim

Respostas:


181

ATUALIZAÇÃO 2018: O C ++ 20 virá com lambdas modeladas e conceituadas. O recurso já foi integrado ao rascunho padrão.


ATUALIZAÇÃO 2014: O C ++ 14 foi lançado este ano e agora fornece aos lambdas polimórficos a mesma sintaxe que neste exemplo. Alguns grandes compiladores já o implementam.


Do jeito que está (em C ++ 11), infelizmente não. Lambdas polimórficas seriam excelentes em termos de flexibilidade e potência.

A razão original pela qual eles acabaram sendo monomórficos foi por causa de conceitos. Os conceitos dificultaram a situação do código:

template <Constraint T>
void foo(T x)
{
    auto bar = [](auto x){}; // imaginary syntax
}

Em um modelo restrito, você pode chamar apenas outros modelos restritos. (Caso contrário, as restrições não poderão ser verificadas.) Pode fooinvocar bar(x)? Quais restrições o lambda possui (afinal, o parâmetro para ele é apenas um modelo)?

Os conceitos não estavam prontos para lidar com esse tipo de coisa; exigiria mais coisas como late_check(onde o conceito não foi verificado até ser invocado) e outras coisas. Mais simples era apenas largar tudo e seguir as lambdas monomórficas.

No entanto, com a remoção de conceitos do C ++ 0x, lambdas polimórficas se tornam uma proposição simples novamente. No entanto, não consigo encontrar nenhuma proposta para isso. :(


5
Simples ... exceto que há um desejo de reintroduzir conceitos e evitar recursos que os tornam complicados.

6
Eu acho que prefiro ter lambdas polimórficas do que conceitos. Não entendo como o exemplo motiva algo; você pode simplesmente proibi-lo como um erro e exigir que o lambda seja monomórfico [] (T x) {} ou um modelo restrito [] modelo <Restrição T> (T x) {}, que pode ser verificado estaticamente para corresponder. Existe alguma razão para isso não ser possível?
DrPizza

13
Você não precisa escolher entre conceitos e lambdas polimórficas: cpp-next.com/archive/2011/12/a-breakthrough-for-concepts
Dave Abrahams

3
Aqui está a proposta para lambdas polimórficas: open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf e a implementação de brinquedos em clang: faisalv.github.com/clang-glambda
Radif Sharafullin

18
Polimórfica Lambdas estará em C ++ 14, pelo menos eles estão no Projecto Comunidade até agora :)
Arne Mertz

37

Os lambdas do C ++ 11 não podem ser modelados conforme indicado em outras respostas, mas decltype()parecem ajudar ao usar um lambda em uma classe ou função modelada.

#include <iostream>
#include <string>

using namespace std;

template<typename T>
void boring_template_fn(T t){
    auto identity = [](decltype(t) t){ return t;};
    std::cout << identity(t) << std::endl;
}

int main(int argc, char *argv[]) {
    std::string s("My string");
    boring_template_fn(s);
    boring_template_fn(1024);
    boring_template_fn(true);
}

Impressões:

My string
1024
1

Descobri que essa técnica é útil ao trabalhar com código de modelo, mas percebo que ainda significa que as próprias lambdas não podem ser de modelo.


26
Tfuncionaria bem no lugar decltype(t)deste exemplo.
usar o seguinte comando

26

No C ++ 11, as funções lambda não podem ser modeladas, mas na próxima versão do ISO C ++ Standard (geralmente chamado C ++ 14), esse recurso será introduzido. [Fonte]

Exemplo de uso:

auto get_container_size = [] (auto container) { return container.size(); };

Observe que, embora a sintaxe use a palavra-chave auto, a dedução de tipo não usará as regras de autodedução de tipo, mas as regras de dedução de argumento do modelo. Consulte também a proposta de expressões lambda genéricas (e a atualização para isso).


5
As regras de autodedução de tipo são definidas especificamente para serem as mesmas da templatededução de argumento de função.
Sublinhado #

10

Estou ciente de que esta pergunta é sobre C ++ 11. No entanto, para aqueles que pesquisaram e pousaram nesta página, os lambdas modelados agora são suportados no C ++ 14 e têm o nome de Lambdas genéricos.

[info] A maioria dos compiladores populares suporta esse recurso agora. Microsoft Visual Studio 2015 suporta. Clang suporta. GCC suporta.


6

Gostaria de saber o que sobre isso:

template <class something>
inline std::function<void()> templateLamda() {
  return [](){ std::cout << something.memberfunc() };
}

Eu usei código semelhante como este, para gerar um modelo e me perguntar se o compilador otimizará a função "quebra automática".


2
Qual compilador? Sim?
NicoBerrogorry


3

Há uma extensão gcc que permite modelos lambda :

// create the widgets and set the label
base::for_each(_widgets, [] <typename Key_T, typename Widget_T>
                         (boost::fusion::pair<Key_T, Widget_T*>& pair) -> void {
                             pair.second = new Widget_T();
                             pair.second->set_label_str(Key_T::label);
                          }
              );

onde _widgetsé umstd::tuple< fusion::pair<Key_T, Widget_T>... >


FWIW, isso se tornou a sintaxe padrão no C ++ 20.
LF

2

Eu tenho jogado com a última version 5.0.1compilação de clang com a -std=c++17bandeira e agora existe um bom suporte para parâmetros de tipo automático para lambdas:

#include <iostream>
#include <vector>
#include <stdexcept>

int main() {
    auto slice = [](auto input, int beg, int end) {
        using T = decltype(input);
        const auto size = input.size();
        if (beg > size || end > size || beg < 0 || end < 0) {
            throw std::out_of_range("beg/end must be between [0, input.size())");
        }
        if (beg > end) {
            throw std::invalid_argument("beg must be less than end");
        }
        return T(input.begin() + beg, input.begin() + end);
    };
    auto v = std::vector<int> { 1,2,3,4,5 };
    for (auto e : slice(v, 1, 4)) {
        std::cout << e << " ";
    }
    std::cout << std::endl;
}

1

Aqui está uma solução que envolve envolver o lamba em uma estrutura:

template <typename T>                                                   
struct LamT                                                             
{                                                                       
   static void Go()                                                     
   {                                                                    
      auto lam = []()                                                   
      {                                                                 
         T var;                                                         
         std::cout << "lam, type = " << typeid(var).name() << std::endl;
      };                                                                

      lam();                                                            
   }                                                                    
};   

Para usar faça:

LamT<int>::Go();  
LamT<char>::Go(); 
#This prints 
lam, type = i
lam, type = c

O principal problema com isso (além da digitação extra) você não pode incorporar essa definição de estrutura em outro método ou obter (gcc 4.9)

error: a template declaration cannot appear at block scope

Eu também tentei fazer isso:

template <typename T> using LamdaT = decltype(                          
   [](void)                                                          
   {                                                                 
       std::cout << "LambT type = " << typeid(T).name() << std::endl;  
   });

Com a esperança de poder usá-lo assim:

LamdaT<int>();      
LamdaT<char>();

Mas eu recebo o erro do compilador:

error: lambda-expression in unevaluated context

Portanto, isso não funciona ... mas mesmo que fosse compilado, seria de uso limitado, porque ainda precisaríamos colocar o "using LamdaT" no escopo do arquivo (porque é um modelo) que meio que derrota o objetivo de lambdas.


1

Não sei por que mais ninguém sugeriu isso, mas você pode escrever uma função de modelo que retorna funções lambda. A seguir, resolvi meu problema, o motivo pelo qual vim a esta página:

template <typename DATUM>
std::function<double(DATUM)> makeUnweighted() {
  return [](DATUM datum){return 1.0;};
}

Agora, sempre que eu quero uma função que aceita um determinado tipo de argumento (por exemplo std::string), eu apenas digo

auto f = makeUnweighted<std::string>()

e agora f("any string")retorna1.0 .

Esse é um exemplo do que quero dizer com "função lambda modelada". (Esse caso específico é usado para fornecer automaticamente uma função de ponderação inerte quando alguém não deseja ponderar seus dados, quaisquer que sejam seus dados.)


2
Isso funcionará apenas se você souber o tipo de argumento para o lambda antes de criar o lambda; nesse caso, você poderá usar apenas um lambda com o tipo específico como argumento. O objetivo do lambda polimórfico é fornecer trabalho a ser realizado em um tipo de argumento que você nunca sabe quando escreve o código de trabalho. Basicamente, isso é totalmente diferente, e é por isso que não foi sugerido.
Klaim

Ah, certo, entendi. Não pensei nesse caso de uso - penso nas funções lambda como coisas on-the-fly e esse tipo de polimorfismo como algo em uma biblioteca multiuso. Eu estava escrevendo uma biblioteca de modelos que precisa aceitar funções lambda do usuário de qualquer tipo e também fornecer padrões do tipo certo.
Jim Pivarski
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.