2017-08-09 23 views
5

Tôi cần tạo một hàm reduce tương tự như std::reduce, nhưng thay vì làm việc trên vùng chứa, chức năng này sẽ hoạt động trên các tham số variadic.Chuyển tiếp và trả về các kiểu trong chức năng giảm chức năng giống như

Đây là những gì tôi hiện có:

template <typename F, typename T> 
constexpr decltype(auto) reduce(F&&, T &&t) { 
    return std::forward<T>(t); 
} 

template <typename F, typename T1, typename T2, typename... Args> 
constexpr decltype(auto) reduce(F&& f, T1&& t1, T2&& t2, Args&&... args) { 
    return reduce(
     std::forward<F>(f), 
     std::forward<F>(f)(std::forward<T1>(t1), std::forward<T2>(t2)), 
     std::forward<Args>(args)...); 
} 

Các công trình sau đây như mong đợi:

std::vector<int> vec; 
decltype(auto) u = reduce([](auto &a, auto b) -> auto& { 
     std::copy(std::begin(b), std::end(b), std::back_inserter(a)); 
     return a; 
    }, vec, std::set<int>{1, 2}, std::list<int>{3, 4}, std::vector<int>{5, 6}); 

assert(&vec == &u); // ok 
assert(vec == std::vector<int>{1, 2, 3, 4, 5, 6}); // ok 

Nhưng sau không làm việc:

auto u = reduce([](auto a, auto b) { 
     std::copy(std::begin(b), std::end(b), std::back_inserter(a)); 
     return a; 
    }, std::vector<int>{}, std::set<int>{1, 2}, 
    std::list<int>{3, 4}, std::vector<int>{5, 6}); 

này về cơ bản bị treo - Để thực hiện công việc này, tôi cần ví dụ thay đổi định nghĩa đầu tiên của reduce thành:

template <typename F, typename T> 
constexpr auto reduce(F&&, T &&t) { 
    return t; 
} 

Nhưng nếu tôi làm như vậy, đoạn mã đầu tiên sẽ không hoạt động nữa.

Vấn đề vấn đề nằm trong việc chuyển tiếp các tham số và kiểu trả về của hàm reduce, nhưng tôi có thể tìm thấy nó.

Tôi nên sửa đổi các định nghĩa reduce của mình để làm cho cả hai đoạn mã hoạt động như thế nào?

+0

Hãy xem các biểu thức gấp C++ 17 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4295.html – Snps

Trả lời

3

Bạn có thể thử

template <typename F, typename T> 
constexpr T reduce(F&&, T &&t) { 
    return std::forward<T>(t); 
} 

này trả về một prvalue khi đối số thứ hai là một rvalue, và một giá trị trái đề cập đến lập luận ngược lại. Đoạn mã của bạn có vẻ là fine with it.

Hoặc, chỉ cần sử dụng biến thể thứ hai của bạn và bọc vec trong std::ref, các sửa đổi thích hợp. Đó cũng là cách tiếp cận tiêu chuẩn khi các mẫu xử lý các đối tượng theo giá trị.

+0

'decltype (auto)' là ma thuật mạnh , người ta phải cực kỳ cẩn thận rằng bạn thực sự * có nghĩa là * nó. +1 – Yakk

2

Lambda trong trường hợp vấn đề của bạn:

[](auto a, auto b) { 
    std::copy(std::begin(b), std::end(b), std::back_inserter(a)); 
    return a; 
} 

lợi nhuận theo giá trị, vì vậy khi reduce recurses:

return reduce(
    std::forward<F>(f), 
    std::forward<F>(f)(std::forward<T1>(t1), std::forward<T2>(t2)), // HERE 
    std::forward<Args>(args)...); 

Đối số thứ hai là một tạm thời khởi tạo từ đối tượng trở lại theo giá trị. Khi đệ quy cuối cùng chấm dứt:

template <typename F, typename T> 
constexpr decltype(auto) reduce(F&&, T &&t) { 
    return std::forward<T>(t); 
} 

Nó trả về một tham chiếu liên kết với đối tượng tạm thời mà bị hủy diệt khi tháo các đệ quy, do đó v được khởi tạo từ một tài liệu tham khảo tòn ten.

Việc sửa chữa dễ dàng nhất cho điều này là không tạo ra một tạm thời trong lambda của bạn và thay vào đó tích lũy các kết quả trong các đối tượng đầu vào mà bạn biết sẽ sống ít nhất cho đến khi kết thúc sự biểu hiện đầy đủ (DEMO):

auto fn = [](auto&& a, auto const& b) -> decltype(auto) { 
    std::copy(std::begin(b), std::end(b), std::back_inserter(a)); 
    // Or better: 
    // a.insert(std::end(a), std::begin(b), std::end(b)); 
    return static_cast<decltype(a)>(a); 
}; 

std::vector<int> vec; 
decltype(auto) u = reduce(fn, vec, 
    std::set<int>{1, 2}, std::list<int>{3, 4}, std::vector<int>{5, 6}); 

assert(&vec == &u); // ok 
assert((vec == std::vector<int>{1, 2, 3, 4, 5, 6})); // ok 

auto v = reduce(fn, std::vector<int>{}, 
    std::set<int>{1, 2}, std::list<int>{3, 4}, std::vector<int>{5, 6}); 
assert((v == std::vector<int>{1, 2, 3, 4, 5, 6})); // ok 
0

Ai đó đã đề cập đến các biểu thức gấp.

template<class F, class T=void> 
struct reduce_t; 

template<class F> 
reduce_t<F> reduce(F&& f); 

template<class F, class T> 
reduce_t<F, T> reduce(F&& f, T&& t); 

template<class F, class T> 
struct reduce_t { 
    F f; 
    T t; 
    template<class Rhs> 
    auto operator|(Rhs&& rhs)&&{ 
    return reduce(f, f(std::forward<T>(t), std::forward<Rhs>(rhs))); 
    } 
    T get()&&{ return std::forward<T>(t); } 
}; 
template<class F> 
struct reduce_t<F,void> { 
    F f; 
    template<class Rhs> 
    auto operator|(Rhs&& rhs)&&{ 
    return reduce(f, std::forward<Rhs>(rhs)); 
    } 
}; 

template<class F> 
reduce_t<F> reduce(F&& f) { 
    return {std::forward<F>(f)}; 
} 

template<class F, class T> 
reduce_t<F, T> reduce(F&& f, T&& t) { 
    return {std::forward<F>(f), std::forward<T>(t)}; 
} 
template<class F, class T, class...Ts> 
auto reduce(F&& f, T&& t, Ts&&...ts) { 
    return (reduce(std::forward<F>(f), std::forward<T>(t)) | ... | std::forward<Ts>(ts)); 
} 

sau đó bất kỳ của những công việc:

decltype(auto) u = (reduce([](auto &a, auto b) -> auto& { 
    std::copy(std::begin(b), std::end(b), std::back_inserter(a)); 
    return a; 
}) | vec | std::set<int>{1, 2} | std::list<int>{3, 4} | std::vector<int>{5, 6}).get(); 

decltype(auto) u = reduce([](auto &a, auto b) -> auto& { 
    std::copy(std::begin(b), std::end(b), std::back_inserter(a)); 
    return a; 
}, vec, std::set<int>{1, 2}, std::list<int>{3, 4}, std::vector<int>{5, 6}).get(); 

auto u_val = (
    reduce([](auto a, auto b) { 
     std::copy(std::begin(b), std::end(b), std::back_inserter(a)); 
     return a; 
    }) 
    | std::vector<int>{} | std::set<int>{1, 2} 
    | std::list<int>{3, 4} | std::vector<int>{5, 6} 
).get(); 

Live example.

Các vấn đề liên quan