Подтвердить что ты не робот

Обобщенный лямбда-захват по параметрам?

В С++ 14 обобщенный захват лямбда позволяет:

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

Но он не играет с param-pack:

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

Есть ли какой-либо специальный синтаксис или дополнительное стандартное предложение для решения этой проблемы?

4b9b3361

Ответ 1

Мой проект С++ 14 говорит ([expr.prim.lambda]/24):

Простой захват, за которым следует многоточие, является расширение пакета (14.5.3). Захват init, сопровождаемый многоточием, плохо сформирован.

Итак, похоже, что нет никакого способа сделать вариационный обобщенный захват. Возможным обходным путем является просто захват аргументов в кортеже, а затем использование одного из предложенных здесь решений: "распаковка" кортеж для вызова соответствующего указателя функции

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

Ответ 2

В качестве продолжения я пришел к этому обходу:

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)...);
};

Он использует mover как прокси-сервер, который позволяет лямбда захватывать его путем перемещения (это немного хаки). И wrap_t решает, когда это необходимо или полезно применить mover.

Теперь мы можем проверить это:

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;

Будет напечатан:

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

Ответ 3

Это расширяет мой комментарий до ответа Брайана выше. В С++ 14 с основами библиотеки TS вы можете:

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

Предположим, вы хотите, чтобы захватить обобщенно параметр обновления по ходу и использовать его в лямбда вы можете написать код в лямбда в лямбда, а затем применить аргументы на него:

[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);
};