2017-08-09 18 views
5

मुझे फ़ंक्शन std::reduce के समान फ़ंक्शन बनाने की आवश्यकता है, लेकिन कंटेनरों पर काम करने की बजाय, इस फ़ंक्शन को वैरिएडिक पैरामीटर पर काम करना चाहिए।फ़ंक्शनल-जैसे कम फ़ंक्शन

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

निम्नलिखित काम करता है के रूप में उम्मीद:

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 

लेकिन निम्नलिखित काम नहीं करता है: - करने के लिए

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

यह मूलतः दुर्घटनाओं

यह मैं वर्तमान में क्या है यह काम करें, मुझे उदाहरण की ज़रूरत है reduce करने के पहले परिभाषा बदलने:

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

लेकिन अगर मैं ऐसा करते हैं, पहले टुकड़ा अब और काम नहीं करता।

समस्या समस्या पैरामीटर को अग्रेषित करने और reduce फ़ंक्शन के रिटर्न प्रकार में निहित है, लेकिन मुझे यह मिल सकता है।

दोनों स्निपेट्स को काम करने के लिए मेरी reduce परिभाषाओं को कैसे संशोधित करना चाहिए?

+0

सी ++ 17 फोल्डिंग एक्सप्रेशन http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4295.html – Snps

उत्तर

3

आप

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

की कोशिश कर सकते यह एक prvalue लौटाता है जब दूसरा तर्क एक rvalue था, और एक lvalue अन्यथा तर्क का जिक्र है। आपके स्निपेट fine with it प्रतीत होते हैं।

वैकल्पिक रूप से, बस अपने दूसरे संस्करण का उपयोग करें और std::ref, mutatis mutandis में लपेटें। यह मानक दृष्टिकोण भी है जब टेम्पलेट्स मूल्यों के आधार पर वस्तुओं को संभालते हैं।

+0

'decltype (auto)' मजबूत जादू है , आपको बेहद सावधान रहना चाहिए कि आप वास्तव में * मतलब * है। +1 – Yakk

2

आपकी समस्या के मामले में लैम्ब्डा:

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

रिटर्न मूल्य से है, इसलिए जब 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)...); 

दूसरा तर्क एक अस्थायी कि दर-मूल्य वापसी वस्तु से प्रारंभ है। जब प्रत्यावर्तन अंत में समाप्त हो जाता है:

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

यह एक संदर्भ है कि अस्थायी वस्तु के लिए बाध्य है, जो इतना है कि v एक झूलने संदर्भ से आरंभ नहीं हो जाता है, जबकि प्रत्यावर्तन तनाव मुक्त नष्ट हो जाता है, देता है।

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 
:

इस के लिए सबसे आसान ठीक नहीं अपने लैम्ब्डा में एक अस्थायी बना सकते हैं और बजाय जमा करने के लिए इनपुट वस्तु में परिणाम जो आप जानते हैं कि पूर्ण अभिव्यक्ति के अंत (DEMO) जब तक कम से कम रहते हैं होगा

0

किसी ने फोल्ड एक्सप्रेशन का उल्लेख किया है।

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

तो इन काम के किसी भी:

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

संबंधित मुद्दे