Como fazer com que as funções do C ++ 11 utilizem a função <> os parâmetros aceitam lambdas automaticamente
C ++ 11 tem lambda e std :: function <>, mas infelizmente, eles têm tipos diferentes. Uma conseqüência é que não se pode usar diretamente lambda em funções de ordem superior, como map in lisp. Por exemplo, no código a seguir
#include <vector>
#include <functional>
using namespace std;
template <typename A,typename B>
vector<B> map(std::function<B (A)> f, vector<A> arr) {
vector<B> res;
for (int i=0;i<arr.size();i++) res.push_back(f(arr[i]));
return res;
}
int main () {
vector<int> a = {1,2,3};
map([](int x) -> int { return x;},a); //not OK
auto id_l = [](int x) -> int { return x;};
map(id_l,a); //not OK;
function<int (int)> id_f = id_l;
map(id_f,a); //OK
return 0;
}
, diretamente usando lambda como na linha 2 do main () não funcionará.g++ -std=c++11 testfunc.cpp
devolve `... testfunc.cpp: 14: 37: note: 'main () :: __ lambda0' não é derivado de 'std :: function'.
A inferência do tipo C ++ 11 também falha, como você pode ver se um armazena o lambda em uma variável automática e depois o usa, as informações do tipo ainda são perdidas, provavelmente devido ao apagamento do tipo e motivos de pequena penalidade de desempenho contou:Por que as funções lambda em c ++ 11 não possuem tipos function <>?).
O que funciona é armazenar o lambda em uma variável std: function <> e usar essa variável. Isso é bastante inconveniente e meio que anula o propósito de usar o lambda na programação funcional em C ++ 11. Por exemplo, não se pode manipular lambda's com coisas como bind ou flip, e em vez disso, armazenar o lambda em uma variável primeiro.
Minha pergunta é, é possível (e como) superar esse problema e tornar a linha # 2 de main () legal, por exemplo, sobrescrevendo alguns operadores de typecast? (Claro, isso significa que eu não me importo com a pequena penalidade de desempenho envolvida em usar / não usar o tipo de apagamento).
desde já, obrigado.
--- EDIT ---
Para esclarecer, a razão pela qual eu usostd::function
em vez de um parâmetro de tipo genérico para o parâmetro funcional é questd::function
tem informações de tipo exatas, enquanto um parâmetro de tipo genérico como emtemplate <typename F> map(F f, ...)
não contém informações de tipo. Além disso, como eu finalmente descobri, cada lambda é seu próprio tipo. Então, o apagamento do tipo não foi nem um problema na incompatibilidade entre um lambda e sua correspondênciastd::function
objeto.
---Atualizar---
Já existem duas respostas sobre como tornar a função do mapa acima do trabalho ou como melhorá-las. Só para esclarecer. Minha pergunta não é sobre como fazer o mapa funcionar. Existem muitos outros casos de uso envolvendo o uso dos parâmetros std :: function <>, o que eu acho que pode pelo menos tornar o código mais legível e facilitar a inferência de tipos. As respostas até agora são sobre como não usar std :: function <> como parâmetros. Minha pergunta foi sobre como fazer tal função (com std :: function <> parâmetros digitados) aceitar lambda automaticamente.
- Atualização 2 ---
Em resposta aos comentários, aqui está um exemplo de caso prático em que as informações de tipo em std :: function <> COULD são úteis. Suponha que queremos implementar um equivalente em C ++fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b
em OCaml (http://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html).
Com std :: function <>, pode-se fazer
//approach#1
template <typename A,typename B>
B fold_right(std::function<B (A, B)> f, vector<A> arr, B b) {
...
}
É claro de cima o quef
é, e o que pode ou não pode tomar. Talvez, também se possa usar
//approach#2
template <typename A,typename B, typename F>
auto fold_right2(F f, vector<A> arr, B b) -> decltype(f(???)) {
...
}
Mas, isso está ficando meio feio quando você tenta descobrir o que colocar nodecltype
. Além disso, o que exatamentef
tomar e qual é a maneira correta de usarf
? Do ponto de vista da legibilidade, acho que o leitor do código só pode descobrir o que é f (uma função ou escalar) e a assinatura de f INTERPRETANDO a implementação no corpo da função.
Isso é o que eu não gosto e é daí que vem minha pergunta. Como fazer a abordagem 1 funcionar convenientemente. Por exemplo, sef
representa adição de dois números, a abordagem # 1 funciona se você criar um objeto de função primeiro:
std::function<int (int, int)> add = [](int x, int y) -> int { return x + y; }
fold_right(add,{1,2,3},0);
Questões de eficiência à parte, o código acima é inconveniente, porque std :: function não pode aceitar lambda. Assim,
fold_right([](int x, int y) -> int { return x + y; },{1,2,3},0);
não funcionará atualmente no C ++ 11. Minha pergunta é especificamente sobre se é possível fazer funções comofold_right
definido acima aceita lambda's diretamente. Talvez seja demais esperar. Espero que isso esclarece a questão.