2014-07-18 16 views
12

Trong C++ 14, khái quát hóa chụp lambda để chúng tôi làm:Chụp lambda tổng quát trên param-pack?

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

Nhưng nó không chơi với param-pack:

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

Có bất kỳ cú pháp đặc biệt hoặc đề nghị tiêu chuẩn hơn nữa để giải quyết vấn đề này?

+1

Bạn đang gặp phải vấn đề gì khi chụp '&' và sử dụng 'f (std :: move (t) ...)'? – chris

+1

@chris Các tài liệu tham khảo sẽ trở nên lúng túng ngay khi 'gói' trả về, không? – Brian

+0

@Brian, Oh yeah, điểm tốt. Tôi không nhận thấy nó đã được trả lại. – chris

Trả lời

16

dự thảo của tôi về C++ 14 nói ([expr.prim.lambda]/24):

Một đơn giản chụp Tiếp theo một dấu chấm lửng là một mở rộng gói (14.5.3). An init-capture theo sau là dấu ba chấm bị hỏng.

Vì vậy, có vẻ như không có cách nào để thực hiện việc chụp tổng quát về biến thể. Một cách giải quyết có thể là chỉ cần nắm bắt được các đối số trong một tuple và sau đó sử dụng một trong những giải pháp gợi ý ở đây: "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

"Chụp bắt đầu theo sau bởi dấu ba chấm bị hỏng", vì vậy tác giả dự thảo biết được việc sử dụng này có thể và không muốn hỗ trợ nó không? hmm ... – Jamboree

+0

@ Jamboree Tôi cho là vậy. Có thể có một số vấn đề kỹ thuật sẽ gây khó khăn cho việc hỗ trợ hoặc có thể bất cứ ai gửi đề xuất bắt đầu muốn giữ đơn giản. – Brian

+3

@Jamboree Đây là cuộc thảo luận: https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/ePRzn4K7VcM – Brian

3

Là một theo dõi, tôi đến workaround này:

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

Nó sử dụng mover làm proxy, cho phép lambda nắm bắt nó bằng cách di chuyển (đó là một chút hacky). Và wrap_t quyết định khi nào là cần thiết hoặc có lợi để áp dụng mover.

Bây giờ chúng ta có thể kiểm tra nó:

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; 

Sẽ in:

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

này mở rộng nhận xét của tôi để Brian câu trả lời ở trên. Trong C++ 14 với nguyên tắc cơ bản thư viện TS bạn có thể:

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

Giả sử bạn muốn quát chụp một gói thông số bằng cách di chuyển và sử dụng nó trong một lambda bạn có thể viết mã trong một lambda trong một lambda, và sau đó áp dụng các đối số trên đó:

[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); 
}; 
Các vấn đề liên quan