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?

Klaim
fonte
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. :(

GManNickG
fonte
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.

Joel
fonte
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).

Timo Türschmann
fonte
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.

RAM
fonte
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".

ted
fonte
2
Qual compilador? Sim?
NicoBerrogorry
4

Dê uma olhada no Boost.Phoenix para lambdas polimórficas: http://www.boost.org/doc/libs/1_44_0/libs/spirit/phoenix/doc/html/index.html Não requer C ++ 0x, pelo maneira :)

usta
fonte
2
Eu já sei sobre isso, mas a questão é exatamente sobre o novo padrão de qualquer maneira;)
Klaim
Ok :) Lambdas C ++ 0x são monomórficas e não podem ser modeladas, infelizmente.
usta
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>... >

user6559931
fonte
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;
}
Doug Coburn
fonte
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.

rmccabe3701
fonte
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.)

Jim Pivarski
fonte
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