2014-07-18 31 views
12

在C++ 14,廣義拉姆達捕獲讓我們做的:param-pack上的通用lambda捕獲?

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

不過,這並不與PARAM包玩:

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

是否有任何特殊的語法或進一步規範提案解決此問題?

+1

你在捕獲'&'和使用'f(std :: move(t)...)時有什麼問題? – chris

+1

@chris只要'pack'返回,引用就會變成懸浮的,不是? – Brian

+0

@布萊恩,噢,好點。我沒有注意到它正在返回。 – chris

回答

16

我的C++ 14草案說([expr.prim.lambda]/24):

簡單捕獲後跟省略號是一包擴展(14.5.3)。一個初始捕獲後跟一個省略號是不合格的。

所以它看起來像沒有辦法做一個可變廣義捕獲。可能的解決方法是剛剛捕獲的參數元組中,然後使用解決方案之一這裏建議:"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

「初始捕獲後跟一個省略號是不合格的」,所以草案作者意識到了這種可能的用法並且不想支持它?嗯... – Jamboree

+0

@Jamboree我想是這樣的。也許有一些技術問題會導致難以支持,或者是提交初始捕獲提案的人想保持簡單。 – Brian

+3

@Jamboree以下是一個討論:https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/ePRzn4K7VcM – Brian

3

作爲後續,我來到這個解決方法:

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作爲代理,它允許lambda通過移動捕獲它(這有點哈克)。 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

它擴展了我的上述布賴恩回答評論。在C++ 14的庫基本面TS您可以:

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

假如你想通過此舉一般拍攝參數組,並用它拉姆達內,你可以一個lambda內寫入一個拉姆達的代碼,然後在其上應用參數:

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