2016-10-27 10 views
33

सी ++ 11 में std::minmax_element कार्य करता है जो मूल्यों की एक जोड़ी देता है। हालांकि, यह संभालने और पढ़ने के लिए काफी भ्रमित है, और दायरे को प्रदूषित करने के लिए एक अतिरिक्त, बाद में बेकार चर का उत्पादन करता है।क्या std :: जोड़े लौटने वाले कार्यों को आसानी से संभालने का कोई तरीका है?

auto lhsMinmax = std::minmax_element(lhs.begin(), lhs.end()); 
int &lhsMin = *(lhsMinMax.first); 
int &lhsMax = *(lhsMinmax.second); 

क्या ऐसा करने का कोई बेहतर तरीका है? कुछ की तरह:

int lhsMin; 
int lhsMax; 
std::make_pair<int&, int&>(lhsMin, lhsMax).swap(
    std::minmax_element(lhs.begin(), lhs.end())); 
+4

कोई भी उल्लेख नहीं किया गया है C++ 17 अभी तक? https://skebanga.github.io/structured-bindings/ – xaxxon

+2

एक प्रश्न: क्या 'minmax_element'' min_axment' और 'max_element' पर एक साथ उपयोग करने में कोई दक्षता लाभ है? –

+2

@xaxxon: सी ++ 17 विशेषताएं सी ++ 11 में नहीं हैं। सवाल सी ++ 11 टैग किया गया है। तो यह एक दिलचस्प स्पर्शक है, लेकिन वास्तविक समाधान नहीं है। –

उत्तर

13

यह एक आम मामले के लिए पर्याप्त एक सहायक समारोह संकेत करने के लिए दिखाई देता है:

template <class T, std::size_t...Idx> 
auto deref_impl(T &&tuple, std::index_sequence<Idx...>) { 
    return std::tuple<decltype(*std::get<Idx>(std::forward<T>(tuple)))...>(*std::get<Idx>(std::forward<T>(tuple))...); 
} 

template <class T> 
auto deref(T &&tuple) 
    -> decltype(deref_impl(std::forward<T>(tuple), std::make_index_sequence<std::tuple_size<std::remove_reference_t<T>>::value>{})) { 
    return deref_impl(std::forward<T>(tuple), std::make_index_sequence<std::tuple_size<std::remove_reference_t<T>>::value>{}); 
} 

// ... 

int lhsMin; 
int lhsMax; 
std::tie(lhsMin,lhsMax) = deref(std::minmax_element(lhs.begin(), lhs.end())); 

index_sequence सी ++ 14 एक पूर्ण कार्यान्वयन can be made in C++11 है, लेकिन।

नोट: मैं decltype को deref में सी ++ 14 में भी रिटर्न प्रकार में रखता हूं, ताकि SFINAE लागू हो सके।

See it live on Coliru

+0

क्या SFINAE किसी भी तरह लागू नहीं होगा? –

+0

@LightnessRacesinOrbit मैंने अभी सुनिश्चित करने के लिए पुनः जांच की है और पुष्टि कर सकते हैं: चूंकि फ़ंक्शन के हस्ताक्षर में कुछ भी 'टी' को प्रतिबंधित नहीं करता है, इसलिए फ़ंक्शन के शरीर को तुरंत चालू करते समय मुझे एक कठिन त्रुटि मिलती है। [यह जल्दी से हैक परीक्षण] देखें (http://coliru.stacked-crooked.com/a/390744d8f0deac2e)। – Quentin

+0

हम्म, अनुमान है कि SFINAE काम नहीं करता है मैंने यह कैसे सोचा था :( –

23

अपने दायरे प्रदूषण से बचने के लिए, आप एक छोटे दायरे में काम लगा सकता है: वैकल्पिक रूप से

int lhsMin, lhsMax; 

{ 
    auto it = std::minmax_element(lhs.begin(), lhs.end()); 
    lhsMin = *it.first; 
    lhsMax = *it.second; 
} 

, आप एक लैम्ब्डा

int lhsMin, lhsMax; 

std::tie(lhsMin, lhsMax) = [&]{ 
    auto it = std::minmax_element(lhs.begin(), lhs.end()); 
    return std::make_tuple(*it.first, *it.second); 
}(); 
+3

यह ध्यान दिया जाना चाहिए कि ओपी के पहले भाग में संदर्भों को निर्दिष्ट करने के लिए न तो काम करेगा। (दूसरा भाग उलझन में है इसलिए यह कहना मुश्किल है लेकिन यह दो 'int & 's असाइन करने पर भी संकेत देता है।) –

+0

संलग्न गुंजाइश के साथ और' std :: tie' मुझे लगता है कि लैम्ब्डा से पढ़ने के लिए आसान है। –

+0

पहले उदाहरण के अंत में अर्धविराम क्यों? – Paul

32
साथ

संरचित उपयोग कर सकते हैं C++ 1z से बाध्यकारी, आप सीधे

auto [lhsMinIt, lhsMaxIt] = std::minmax_element(lhs.begin(), lhs.end()); 
+2

साफ! क्या आप एक लाइन में इटेटरेटर जोड़ी से वैल्यू जोड़ी भी प्राप्त कर सकते हैं? – davmac

+2

@AdamHunyadi: C++ 1z "यह औपचारिक रूप से अस्तित्व में नहीं है" सी ++ 17 के लिए नाम है, जैसे सी ++ 0x सी ++ 11 (देर से!) और सी ++ 1y बन गया क्योंकि सी ++ 14। –

+9

उपरोक्त क्योंकि यह एक दिलचस्प चर्चा बिंदु है, और _will_ एक अच्छा समाधान है, लेकिन यह वास्तव में प्रश्न का उत्तर नहीं देता है (जो सी ++ 11 के बारे में है)। –

4

मैं सिर्फ और अधिक प्रत्यक्ष हो सकता है और minmax_element के अपने खुद के संस्करण लिखते हैं:

template <class Iter, class R = typename iterator_traits<Iter>::reference> 
std::pair<R,R> deref_minmax(Iter first, Iter last) 
{ 
    auto iters = std::minmax_element(first, last); 
    return std::pair<R,R>{*iters.first, *iters.second}; 
} 

कौन सा तो बस है:

int lo, hi; 
std::tie(lo, hi) = deref_minmax(lhs.begin(), lhs.end()); 

यह आप के लिए सिर्फ एक सीमित हो जाएगी तत्वों की एक प्रति (जो कि int एस के साथ एक बड़ा सौदा नहीं है), आपको वें संदर्भों तक पहुंच बनाए रखने देता है ई वास्तविक कंटेनर।

template <class Tuple> 
auto deref(Tuple&& tup) { 
    return std::apply([](auto... args) { 
     return std::tuple <decltype(*args)...>(*args...); 
    }, tup); 
} 

auto& [lo, hi] = deref(std::minmax_element(lhs.begin(), lhs.end())); 

यहाँ lo और hi कंटेनर में ही संदर्भ हैं:


सी ++ 17 में, मनोरंजन के लिए, हम एक सामान्यीकृत dereferencer लिख सकते हैं।

+0

आपके 'deref' फ़ंक्शन के परिणामस्वरूप अपरिभाषित व्यवहार हो सकता है। यदि इटरेटर्स (या जो भी 'तर्क' समाप्त होता है) रिटर्न प्राप्त करते हैं, तो आपको लटकने वाले संदर्भ मिलते हैं, क्योंकि 'std :: forward_as_tuple' उनके लिए एक रावल्यू संदर्भ देता है। – Xeo

+0

@Xeo कॉलर पर निर्भर करता है कि संदर्भ न रखें। – Barry

+2

गलत। आपको 'वापसी std :: forward_as_tuple (...); 'बिंदु पर लापरवाही संदर्भ मिलते हैं, क्योंकि उस क्षेत्र में अस्थायी बनाए जाते हैं और जैसे ही फ़ंक्शन लौटाता है, मृत हो जाते हैं। – Xeo

2

मानक के वर्तमान संशोधन में एक बार में दो संदर्भ असाइन करने का कोई तरीका नहीं है, यदि आप यही कर रहे हैं। ध्यान दें कि अन्य उत्तरों में से कोई भी ऐसा नहीं करता है, बैरी को छोड़कर जिसके लिए सी ++ 17 और एक सहायक टेम्पलेट की आवश्यकता होती है।

हालांकि, यदि आप अपने न्यूनतम और अधिकतम तत्वों को पढ़ने-लिखने की पहुंच चाहते हैं, तो क्यों न केवल minmax_element आपको सीधे प्रदान करता है? वैसे भी संदर्भों का उपयोग करने के समान समान कोड कोड उत्पन्न करने की संभावना है, कम से कम यदि आपका lhsContiguousContainer है लेकिन शायद अन्य मामलों में भी।

उदाहरण के लिए आप स्वत: प्रकार कटौती पर कम थोड़ा भरोसा करने के लिए, की आवश्यकता होगी,

decltype(lhs.begin()) lhsMinIt, lhsMaxIt; 
std::tie(lhsMinIt, lhsMaxIt) = std::minmax_element(lhs.begin(), lhs.end()); 
/* now access your minimum and maximum as *lhsMinIt and *lhsMaxIt */ 

क्या आप जानते हैं lhs के प्रकार के मानक कंटेनर में से एक होगी, तो आप थोड़ा क्लीनर प्रकार उपयोग कर सकते हैं पदनाम decltype(lhs)::iterator

2

C++ में 14 या अधिक से अधिक

template<class=void, std::size_t...Is> 
auto indexer(std::index_sequence<Is...>) { 
    return [](auto&&f){ 
    return f(std::integral_constant<std::size_t, Is>{}...); 
    }; 
} 
template<std::size_t N> 
auto indexer() { 
    return indexer(std::make_index_sequence<N>{}); 
} 
template<class F> 
auto fmap_over_tuple(F&& f) { 
    return [f=std::forward<F>(f)](auto&& tuple) { 
    using Tuple = decltype(tuple); 
    using Tuple_d = std::decay_t<Tuple>; 
    auto index = indexer< std::tuple_size<Tuple_d>::value >(); 
    return index(
     [&f, &tuple](auto&&...Is) { 
     using std::get; 
     return std::make_tuple(
      f(get<Is>(std::forward<Tuple>(tuple)))... 
     ); 
     } 
    ); 
    }; 
} 

तो fmap_over_tuple एक समारोह वस्तु लेता है। यह एक फ़ंक्शन ऑब्जेक्ट देता है कि, जब एक ट्यूपल-जैसे पारित किया जाता है, तो टुपल की तरह प्रत्येक तत्व पर फ़ंक्शन ऑब्जेक्ट को कॉल करने के लिए आय प्राप्त होती है, और इससे एक ट्यूपल उत्पन्न होती है।

हम तो भिन्नता टपल लिखें:

auto dereference_tuple = fmap_over_tuple(
    [](auto&& e) { return *e; } 
); 
सी ++ 17 में

अब हम कार्य करें:

auto[Min, Max] = dereference_tuple(std::minmax_element(lhs.begin(), lhs.end()); 

और बॉब अपने चाचा है।

सी ++ 11 में, बस आपने जो किया वह करें। पर्याप्त साफ करो।

C++14 live example

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

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