2016-02-18 21 views
6

Considera la seguente serie di esempi.È possibile acquisire un numero variabile di parametri in una lambda?

  1. La funzione takeOnlyVoidFunction accetta una funzione con argomenti zero e semplicemente la esegue.
  2. La funzione takeVariableArguments accetta un numero variabile di argomenti ed esegue la funzione utilizzando gli argomenti.
  3. La funzione captureVariableArgs tenta di convertire la seconda funzione in una forma lambda accettabile dalla prima funzione, ma non viene compilata.

Come posso fare la compilazione funzione di captureVariableArgs ed esibire il comportamento corretto di conversione di una funzione con un numero variabile di argomenti in una chiusura senza argomenti?

#include <stdio.h> 
#include <functional> 

void takeOnlyVoidFunction(std::function<void()> task) { 
    task(); 
} 

template<typename _Callable, typename... _Args> 
    void takeVariableArguments(_Callable&& __f, _Args&&... __args) { 
    __f(__args...); 
} 

// How can I make this function compile? 
template<typename _Callable, typename... _Args> 
    void captureVariableArgs(_Callable&& __f, _Args&&... __args) { 
    takeOnlyVoidFunction([=]() { __f(__args...);}); 
} 

void normalFunction(int a, int b) { 
    printf("I am a normal function which takes params (%d,%d)\n", a, b); 
} 

int main() { 
    int a = 7; 
    int b = 8; 
    takeVariableArguments(normalFunction, a, b); 
    takeOnlyVoidFunction([=](){ normalFunction(a,b);}); 
    captureVariableArgs(normalFunction, a, b); 
} 

sto correndo gcc 4.9.2. Ecco l'errore del compilatore che vedo.

g++ -std=c++11 Test.cc -o Test 
Test.cc: In instantiation of ‘captureVariableArgs(_Callable&&, _Args&& ...)::<lambda()> [with _Callable = void (&)(int, int); _Args = {int&, int&}]’: 
Test.cc:16:38: required from ‘struct captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’ 
Test.cc:16:50: required from ‘void captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]’ 
Test.cc:28:45: required from here 
Test.cc:16:34: error: variable ‘__f’ has function type 
    takeOnlyVoidFunction([=]() { __f(__args...);}); 
           ^
Test.cc:16:34: error: variable ‘__f’ has function type 
Test.cc: In instantiation of ‘struct captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’: 
Test.cc:16:50: required from ‘void captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]’ 
Test.cc:28:45: required from here 
Test.cc:16:34: error: field ‘captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>::<__f capture>’ invalidly declared function type 
In file included from Test.cc:2:0: 
/usr/include/c++/4.9/functional:2418:7: error: ‘std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>; <template-parameter-2-2> = void; _Res = void; _ArgTypes = {}]’, declared using local type ‘captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’, is used but never defined [-fpermissive] 
     function<_Res(_ArgTypes...)>:: 
    ^

Aggiornamento: Un esempio più minimal che dimostra questo problema.

#include <stdio.h> 

// How can I make this function compile? 
template<typename _Callable> 
void captureVariableArgs(_Callable&& __f) { 
    takeOnlyVoidFunction([=]{ __f(); }); 
} 

void normalFunction() { 
    printf("I am a normal function\n"); 
} 

int main(){ 
    captureVariableArgs(normalFunction); 
} 
+1

Il tuo codice compila e funziona, qual è il problema che vedi? – ixSci

+0

La funzione con il commento non viene compilata per me. Sei in clang o gcc? – merlin2011

+0

Si aggiornerà con errori di compilazione in un paio di minuti. – merlin2011

risposta

2

Come un altro potenziale soluzione per GCC, invece di utilizzare un lambda, è possibile utilizzare std::bind:

template <typename F, typename... Args> 
auto captureVariable(F&& f, Args&&... args) 
{ 
    return std::bind(std::forward<F>(f), std::forward<Args>(args)...); 
} 

Questo funziona per me sotto GCC 4.9.3.

+0

In realtà, ho preso in considerazione anche questo e lo ha compilato, ma non ero sicuro se fosse equivalente. È semanticamente equivalente? – merlin2011

+0

Non è abbastanza semanticamente equivalente al tuo esempio, mentre copi gli argomenti quando usi '[=]'; questo dovrebbe effettivamente inoltrarli. – Yuushi

+0

@Yuushi, credo che 'bind' copierà tutti i suoi argomenti. Deve memorizzarli in modo che non possano solo inoltrarli. – ixSci

2

Il codice nel post compila bene con le ultime clangore & MSVC compilatori gcc ma tutte le rifiutano di compilarlo. Quindi sembra un bug in gcc. Tuttavia, ho trovato un modo per rendere felice gcc: basta non usare un "riferimento universale" sull'argomento callable, in questo modo:

template<typename _Callable, typename... _Args> 
int captureVariableArgs(_Callable _f, _Args&&... _args) { 
    return takeOnlyVoidFunction([=]() { _f(_args...);}); 
} 

non riesco a spiegare il motivo per cui gcc non accetta la versione, anche se. Non ho familiarità con la segnalazione degli errori di gcc-style e non posso estrarre la vera causa dal messaggio di errore. Ma penso che la soluzione è ok dato che in questo caso non vedo alcun valore in "riferimento universale". In effetti, non vedo perché lo usi anche sugli args.

+0

Uso un riferimento universale perché sto implementando una libreria di threading e l'implementazione di 'std :: thread' in gcc utilizza un riferimento universale per gli argomenti e suppongo che lo faccia per prestazioni e flessibilità. – merlin2011

+0

Beh, è ​​copiato comunque dal lambda con '[=]'. Forse puoi spostarlo in qualche modo nella lista di cattura, mantenendo così solo una copia con tipi mobili. – PeterT

+0

@ merlin2011, in questa funzione non ha senso - basta copiarlo con la lista di cattura – ixSci

Problemi correlati