2016-01-26 15 views
7

Chcę utworzyć lambda, która będzie przyjmować dowolną liczbę argumentów, takich jak:Przenieś wszystkie argumenty lambda

template <typename... Args> 
void f(Args... args) { 
    auto l = [args...]() { 
     g(args...); 
    } 
    // use l 
} 

problem polega na tym, że nie działa w ruch tylko do typów. Jeśli byłby to tylko 1 argument, zrobiłbym coś podobnego do:

void f(Arg arg) { 
    auto l = [arg = std::move(arg)]() { 
     g(move(arg)); 
    } 
} 

Jak przenieść wszystkie argumenty do lambda?

Odpowiedz

8
template <class... Args> 
void f(Args... args) { 
    auto l = [tup=std::make_tuple(std::move(args)...)] { 
    std::apply([](auto&&...args){ 
     g(decltype(args)(args)...); 
    }, tup); 
    }; 
} 

Trochę icky.

Zapakuj je do krotki, a następnie rozpakuj krotkę przy pomocy std::apply. Jeśli brakuje Ci std::apply napisz sobie odpowiednik.

Jeśli chcesz wywołać g z wartościami r, ustaw zewnętrzną zmienną lambda i move tuple w wewnętrzną lambdę.

Wewnętrzna lambda może przechwycić domyślnie &, jeśli chcesz uzyskać dostęp do argumentów zewnętrznych lub podobnych.

Możemy nawet abstrakcyjny wzór ten nieco:

template<class F, class...Args> 
auto forward_capture(F&& f, Args&&...args) { 
    return [ 
    f=std::forward<F>(f), 
    tup=std::make_tuple(std::forward<Args>(args)...) 
    ]{ 
    return std::apply(f, tup); 
    }; 
} 

zastosowanie:

template <typename... Args> 
void f(Args... args) { 
    auto l = forward_capture(
    [](auto&&...args) { 
     g(args...); 
    }, 
    std::move(args)... 
); 
    // use l 
} 

Jeśli chcesz najpierw listę przechwytywania, możemy to zrobić:

template<class...Args> 
auto forward_capture(Args&&...args) { 
    return [ 
    tup=std::make_tuple(std::forward<Args>(args)...) 
    ](auto&& f)mutable{ 
    return [ 
     f=decltype(f)(f), 
     tup=std::move(tup) 
    ]{ 
     return std::apply(f, tup); 
    }; 
    }; 
} 

korzystania :

template <typename... Args> 
void f(Args... args) { 
    auto l = forward_capture(std::move(args)...)(
    [](auto&&...args) { 
     g(args...); 
    } 
); 
    // use l 
} 

który ma "zaletę", że mamy 3 zagnieżdżone lambdy.

Albo więcej zabawy:

template<class...Args> 
struct arrow_star { 
    std::tuple<Args...> args; 
    template<class F> 
    auto operator->*(F&& f)&& { 
     return [f=std::forward<F>(f),args=std::move(args)]()mutable{ 
     return std::experimental::apply(std::move(f), std::move(args)); 
     }; 
    } 
}; 
template<class...Args> 
arrow_star<std::decay_t<Args>...> forward_capture(Args&&...args) { 
    return {std::make_tuple(std::forward<Args>(args)...)}; 
} 
template<class...Args> 
auto f(Args... args) 
{ 
    return 
    forward_capture(std::move(args)...) 
    ->* 
    [](auto&&...args){ 
     g(decltype(args)(args)...); 
    }; 
} 

live example.

+0

Nie tak piękne, ale działa :) – RiaD

+0

@RiaD gdy jestem na tym, oto 2 warianty. ;) – Yakk

+0

Mam pytanie dotyczące przechwytywania za pomocą inicjalizatorów. Czytałem, że są one głównie dla typów typu "ruch", ale nie może być alternatywą dla zrobienia '[x = std :: move (x)], aby wziąć referencję, a następnie przenieść ->' [&] {std: : move (x); } '? Przeniesienie go na listę przechwytywania wydaje się zbędne dla moich niewprawnych oczu. – 0x499602D2