2014-07-18 11 views
12

In C++ 14, cattura lambda generalizzata facciamolo:Cattura lambda generalizzata su param-pack?

template<class T> 
auto pack(T t) 
{ 
    return [t=std::move(t)](auto&& f){f(t);}; 
}; 

Ma non gioca con param-pack:

template<class... T> 
auto pack(T... t) 
{ 
    return [t=std::move(t)...](auto&& f){f(t...);}; 
}; 

C'è una sintassi speciale o proposta più standard affrontare questo?

+1

Che problema si ha nel catturare '&' e usando 'f (std :: move (t) ...)'? – chris

+1

@chris I riferimenti diventerebbero penzoloni non appena "pack" ritorna, no? – Brian

+0

@ Brian, oh si, buon punto. Non ho notato che veniva restituito. – chris

risposta

16

Il mio progetto di C++ 14 dice ([expr.prim.lambda]/24):

A semplice-capture seguito da puntini di sospensione è un pacchetto di espansione (14.5.3). Un init-capture seguito da un'ellissi è mal formato.

Quindi sembra che non ci sia modo di fare una cattura generalizzata variadica. Una possibile soluzione è quella di catturare solo gli argomenti in una tupla e quindi utilizzare una delle soluzioni proposte qui: "unpacking" a tuple to call a matching function pointer

auto pack(T... t) 
{ 
    return [args=make_tuple(std::move(t)...)](auto&& f){ 
       // find a way to call f with args 
      }; 
}; 
+1

"Un init-capture seguito da un puntino di sospensione è mal formato", quindi l'autore della bozza è a conoscenza di questo possibile utilizzo e non intende supportarlo? hmm ... – Jamboree

+0

@Jamboree Suppongo di sì. Forse ci sono alcuni problemi tecnici che renderebbero difficile il supporto, o forse chiunque abbia presentato la proposta per init-capture voleva mantenerlo semplice. – Brian

+3

@Jamboree Ecco una discussione: https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/ePRzn4K7VcM – Brian

3

Come follow-up, sono arrivato a questa soluzione:

template<class T> 
struct mover 
{ 
    mover(T const& val) : val(val) {} 

    mover(T&& val) : val(std::move(val)) {} 

    mover(mover const& other) = default; 

    mover(mover&& other) = default; 

    mover(mover& other) : val(std::move(other.val)) {} 

    operator T const&() const 
    { 
     return val; 
    } 

    T val; 
}; 

template<class T> 
using wrap_t = typename std::conditional 
    < 
     std::is_move_constructible<T>::value 
    && !std::is_trivially_copy_constructible<T>::value 
     , mover<T> 
     , T 
    >::type; 

template<class... Ts> 
auto pack_impl(wrap_t<Ts>... ts) 
{ 
    return [=](auto&& f)->decltype(auto) 
    { 
     return f(static_cast<Ts const&>(ts)...); 
    }; 
} 

auto pack = [](auto&&... ts) 
{ 
    return pack_impl<std::decay_t<decltype(ts)>...>(static_cast<decltype(ts)>(ts)...); 
}; 

E ' utilizza mover come proxy, che consente a lambda di acquisirlo tramite move (è un po 'hacky). E wrap_t decide quando è necessario o utile applicare mover.

Ora possiamo testarlo:

struct A 
{ 
    A() = default; 

    A(A&&) 
    { 
     std::cout << "move\n"; 
    } 

    A(A const&) 
    { 
     std::cout << "copy\n"; 
    } 
}; 

A a; 
std::cout <<"p1------------\n"; 
auto p1 = pack(std::move(a)); 
std::cout <<"p2------------\n"; 
auto p2 = std::move(p1); 
std::cout <<"p3------------\n"; 
auto p3 = p2; 

stamperà:

p1------------ 
move 
move 
p2------------ 
move 
p3------------ 
copy 
3

Questo si espande su mio commento a Brian risposta di cui sopra. In C++ 14 con i fondamenti della biblioteca TS è possibile:

template<class... T> 
auto pack(T... t) 
{ 
    return [ts = std::make_tuple(std::move(t)...)](auto&& f){ 
     std::experimental::apply(f, ts); 
    }; 
}; 

Supponendo che si desidera catturare genericamente un parametro pack mossa e utilizzarlo all'interno di un lambda è possibile scrivere il codice in un lambda all'interno di un lambda, e quindi applicare gli argomenti su di esso:

[ts = std::make_tuple(std::move(t)...)](/* args */){ 
    auto lambda = [&](auto&&... args) { 
     // - args is the original parameter pack that one wanted to 
     // capture by move 
     // - the other arguments can be used by reference 
    }; 
    return std::experimental::apply(lambda, ts); 
}; 
Problemi correlati