2016-04-27 18 views
7

Ho un modello che accetta una funzione come argomento.Come posso passare l'espressione lambda al modello C++ come parametro

Quando si tenta di passare un'espressione lambda non viene compilata.

typedef int (*func)(int a); 
template <func foo> 
int function(int a) 
{ 
    foo(a); 
} 

int test(int a) 
{ 
    return a; 
} 

int main() 
{ 
    function<test>(1); // ---> this is ok 

    auto lambda = [](int a) -> int { return a; }; 
    function<lambda>(1); // ---> this is wrong, why? 

    return 0; 
} 

Cosa mi manca?

+0

'funzione (1);' non è 'ok'. I modelli richiedono un tipo o una costante intera come parametri. 'test' non è né E nessuno dei due è 'lambda'. – DeiDei

+0

è ok, controlla questa risposta http://stackoverflow.com/questions/1174169/function-passed-as-template-argument/2156899#2156899 e puoi provarlo pure – gsf

+0

lambda è già std :: tipo di funzione. Quello che stai facendo sembra funzione )> (1) che non è valido (perché 1 non è una funzione int). – Striker

risposta

11

Un lambda non è un puntatore di funzione! Un lambda è un'istanza della classe generata dal compilatore!

Tuttavia, un lambda non cattura può essere convertito in un puntatore a funzione usando il suo operator+

Ecco un esempio:

int main() { 
    auto lambda = [](int a) { return a; }; 

    func ptr = +lambda; // this would work 

    return 0; 
} 

Purtroppo, il operator+ sarà nemmeno lavorare nel tuo caso, perché ha non è stato dichiarato come constexpr, quindi non puoi usarlo in un parametro template.

Una correzione per il tuo caso sarebbe quella di utilizzare una funzione gratuita ... fino a quando N4487 non è accettato, non puoi aspettarti di passare lambda come parametro del modello.

Un'altra soluzione potrebbe essere quella di creare il proprio funtore invece di un lambda:

struct LambdaType { 
    constexpr LambdaType() = default; 

    int operator()(int a) { 
     return run(a); 
    } 

    // this is a non-capturing lambda, the operator can be 
    // in a static function 
    static int run(int a) { 
     return a; 
    } 
}; 

int main() { 
    LambdaType lambda; 

    function<&LambdaType::run>(1); // ---> this is working 

    return 0; 
} 

questa soluzione non è molto attraente, ma potrebbe essere utile se LambdaType è nascosto in un file cpp.

Se il vostro obiettivo è solo il compilatore per essere in grado di inline codice, è possibile utilizzare i modelli per passare il lambda intorno:

#include <iostream> 

template <typename T> 
int function(T foo, int a) { 
    return foo(a); 
} 

int main() { 
    int a; 
    std::cin >> a; 

    int b = function([](int a) { return a; }, a); 

    return b; 
} 

Dal momento che il compilatore conosce il tipo di T per ogni instanciation, un buon il compilatore dovrebbe essere in grado di ottimizzare il lambda.

Con clang, la terza opzione ha pronunciato la seguente assemblea:

main:        # @main 
    pushq %rax 
    leaq 4(%rsp), %rsi 
    movl std::cin, %edi 
    callq std::basic_istream<char, std::char_traits<char> >::operator>>(int&) 
    movl 4(%rsp), %eax # this is the call to the function 
    addq $8, %rsp 
    retq 

    pushq %rax 
    movl std::__ioinit, %edi 
    callq std::ios_base::Init::Init() 
    movl std::ios_base::Init::~Init(), %edi 
    movl std::__ioinit, %esi 
    movl $__dso_handle, %edx 
    popq %rax 
    jmp  __cxa_atexit   # TAILCALL 

ho usato -std=c++14 -Ofast -march=native come bandiere.

+1

Sì, inserendo la funzione è tutto ciò che voglio, lasciami provare la terza opzione. – gsf

2

Non conosco abbastanza lo standard per dire se questo è il mio errore dei compilatori non implementarlo correttamente o se è effettivamente lo standard, ma con VS2015 non è possibile generare un'espressione lambda costante in fase di compilazione. E i template prendono solo costanti di tempo compilate, quindi nessun lambda.

Tuttavia, non è necessario essere un modello se si desidera passare un lambda. È perfettamente possibile senza:

#include <functional> 

int function(std::function<int(int)> const& f, int a) 
{ 
    f(a); 
} 

int test(int a) 
{ 
    return a; 
} 

int main() 
{ 
    auto lambda = [](int a) -> int { return a; }; 

    function(test, 1); 
    function(lambda, 1); 

    return 0; 
} 
+0

Non è colpa del compilatore. Speriamo che il tempo di compilazione lambdas diventi una cosa con C++ 17! – DeiDei

+0

Non è quello che voglio, però. Mi piacerebbe che questo fosse un argomento di template in modo che la funzione per essere eventualmente inline al posto del puntatore fosse sempre chiamata runtime. – gsf

6

Questo perché il lambda è di tipo proprio.
Hai templatize function() sul tipo di funzione passata.

template<typename F> 
int function(F foo, int a) { 
    return foo(a); 
} 

int test(int a) { 
    return a; 
} 

int main() 
{ 
    // function will work out the template types 
    // based on the parameters. 
    function(test, 1); 
    function([](int a) -> int { return a; }, 1); 
} 
Problemi correlati