2017-08-09 38 views
5

std::reduce benzeri bir reduce işlevi oluşturmam gerekiyor, ancak kapsayıcılar üzerinde çalışmak yerine, bu işlev variadic parametreler üzerinde çalışmalıdır. beklendiği gibiİşlevsel olarak küçültme işlevinde ileri ve geri dönüş tipi (ler)

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

aşağıdaki işleri:

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 

Ancak aşağıdaki çalışmaz: - To

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

Bu temelde çöküyor

Bu

Şu anda ne var Bu işi yapmak, örneğin

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

Ama bunu yaparsanız, ilk pasajı artık çalışmıyor: reduce için ilk tanımını değiştirebilir.

Sorun, parametrelerin iletilmesinde ve reduce işlevinin dönüş türünde yatar, ancak ben onu bulabilirim.

Her iki parçacığı da çalışmak için reduce tanımlarımı nasıl değiştirmeliyim?

+0

C++ 17 katlama ifadelerine bakın http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4295.html – Snps

cevap

3

İkinci argüman bir rvalue yaparken bu iş prvalue döndüren

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

deneyebilirsiniz ve bir lvalue aksi argüman atıfta. Snippet'leriniz fine with it olarak görünüyor.

Alternatif olarak, ikinci varyantınızı kullanın ve std::ref, mutatis mutandis ürününü sarın. Ayrıca, şablonlar nesneleri nesneye göre ele aldığında standart yaklaşımdır.

+0

'decltype (auto)' güçlü bir sihirdir Birisi, aslında * demek istediğin için çok dikkatli olmalı. +1 – Yakk

2

Sorun durumda lambda değeri

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

döner, böylece reduce Recurses zaman:

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

ikinci bağımsız Bununla değer iade nesneden başlatılan bir geçicidir. Yineleme sonunda sonlandırıldığında:

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

Bu özyinelemeye dinlenmek ise v bir sarkan referanstan başlatılır, böylece tahrip olduğu geçici nesneye bağlı bir başvuru verir.

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 
:

bunun için en kolay düzeltme tam ifadenin sonunda (DEMO) kadar en az yaşayacak biliyorum girdi nesnesi sonuçları sizin lambda geçici oluşturmak ve bunun yerine birikir DEĞİL olacaktır

0

Birisi kat ifadelerinden söz etti.

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

bu işin ardından herhangi bir:

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.