क्या std का उपयोग करना मान्य है :: std के साथ रूपांतरण :: back_inserter?


20

Cppreference में इसके लिए उदाहरण कोड है std::transform:

std::vector<std::size_t> ordinals;
std::transform(s.begin(), s.end(), std::back_inserter(ordinals),
               [](unsigned char c) -> std::size_t { return c; });

लेकिन यह भी कहता है:

std::transformunary_opया के आदेश आवेदन की गारंटी नहीं है binary_op। किसी फंक्शन को किसी सीक्वेंस-ऑर्डर में लागू करने के लिए या किसी फंक्शन को लागू करने के लिए जो किसी सीक्वेंस के एलिमेंट्स को मॉडिफाई करता है std::for_each

यह संभवतः समानांतर कार्यान्वयन की अनुमति देने के लिए है। हालाँकि तीसरा पैरामीटर std::transformवह है LegacyOutputIteratorजिसके लिए निम्नलिखित पोस्टकंडिशन है ++r:

इसके बाद ऑपरेशन rको बढ़ाने के लिए आवश्यक नहीं है और पिछले मूल्य की किसी भी प्रतियां को rअब अनुमेय या वृद्धि योग्य होने की आवश्यकता नहीं है।

तो यह मुझे लगता है कि आउटपुट का असाइनमेंट क्रम में होना चाहिए । क्या उनका सीधा मतलब यह है कि आवेदन के unary_opक्रम से बाहर हो सकता है, और एक अस्थायी स्थान पर संग्रहीत किया जा सकता है, लेकिन आदेश में आउटपुट पर कॉपी किया जाता है? ऐसा कुछ नहीं लगता है जिसे आप कभी करना चाहते हैं।

अधिकांश C ++ पुस्तकालयों ने वास्तव में समानांतर निष्पादकों को अभी तक लागू नहीं किया है, लेकिन Microsoft के पास है। मुझे पूरा यकीन है कि यह प्रासंगिक कोड है, और मुझे लगता है कि यह इस populate()फ़ंक्शन को आउटपुट के चांस को रिकॉर्ड करने के लिए कॉल करता है, जो निश्चित रूप से ऐसा करने के लिए एक वैध बात नहीं है क्योंकि LegacyOutputIteratorइसकी प्रतियां बढ़ाकर अमान्य किया जा सकता है।

मैं क्या खो रहा हूँ?


गॉडबोल्ट में एक साधारण परीक्षण से पता चलता है कि यह एक मुद्दा है। सी ++ 20 और transformसंस्करण के साथ जो यह तय करता है कि क्या लकवा का उपयोग करना है या नहीं। transformबड़े वैक्टर के लिए विफल रहता है।
क्रोलमैन

6
@ क्रोलमैन आपका कोड गलत है, क्योंकि आप बैक-इंसर्ट कर रहे हैं s, जो पुनरावृत्तियों को अमान्य करता है।
डैनियल लैंगर

@DanielsaysreinstateMonica ओह schnitzel तुम सही हो। इसे तोड़-मरोड़ कर गैर-मान्य स्थिति में छोड़ दिया गया था। मैं अपनी टिप्पणी वापस लेता हूं।
क्रोलमैन

यदि आप std::transformसटीक नीति के साथ उपयोग करते हैं तो यादृच्छिक अभिगमकर्ता की आवश्यकता होती है जो back_inserterपूरा नहीं कर सकता है। आईएमओ उद्धृत भाग प्रलेखन उस परिदृश्य को संदर्भित करता है। प्रलेखन उपयोग में नोट उदाहरण std::back_inserter
मर्क आर

@ क्रोलमैन स्वचालित रूप से समानता का उपयोग करने का निर्णय लेता है?
जिज्ञासु

जवाबों:


9

1) मानक में आउटपुट इटेटर की आवश्यकताएं पूरी तरह से टूट गई हैं। LWG2035 देखें ।

2) यदि आप विशुद्ध रूप से आउटपुट इटरेटर और विशुद्ध रूप से इनपुट स्रोत रेंज का उपयोग करते हैं, तो एल्गोरिथ्म में कुछ और भी हो सकता है; यह क्रम में लिखने के अलावा कोई विकल्प नहीं है। (हालांकि, एक काल्पनिक कार्यान्वयन अपने स्वयं के प्रकारों को विशेष-मामले में चुन सकता है, जैसे std::back_insert_iterator<std::vector<size_t>>; मैं नहीं देखता कि कोई कार्यान्वयन यहाँ क्यों करना चाहेगा, लेकिन उसे ऐसा करने की अनुमति है।)

3) मानक गारंटी में कुछ भी नहीं है transformजो परिवर्तनों को क्रम में लागू करता है। हम एक कार्यान्वयन विवरण देख रहे हैं।

इसके std::transformलिए केवल आउटपुट पुनरावृत्तियों की आवश्यकता नहीं है, इसका मतलब यह नहीं है कि यह उच्चतर इट्रेटर स्ट्रेंथ का पता लगा सकता है और ऐसे मामलों में संचालन को फिर से व्यवस्थित कर सकता है। दरअसल, एल्गोरिदम इटरेटर बल पर प्रेषण हर समय है, और वे (संकेत या वेक्टर iterators की तरह) विशेष इटरेटर प्रकार के लिए विशेष हैंडलिंग है हर समय

जब मानक किसी विशेष आदेश की गारंटी देना चाहता है, तो यह जानता है कि इसे कैसे कहा जाए (देखें std::copy"से शुरू हो रहा है firstऔर आगे बढ़ रहा है last")।


5

से n4385:

§25.6.4 रूपांतरण :

template<class InputIterator, class OutputIterator, class UnaryOperation>
constexpr OutputIterator
transform(InputIterator first1, InputIterator last1, OutputIterator result, UnaryOperation op);

template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2, class UnaryOperation>
ForwardIterator2
transform(ExecutionPolicy&& exec, ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 result, UnaryOperation op);

template<class InputIterator1, class InputIterator2, class OutputIterator, class BinaryOperation>
constexpr OutputIterator
transform(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, OutputIterator result, BinaryOperation binary_op);

template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2, class ForwardIterator, class BinaryOperation>
ForwardIterator
transform(ExecutionPolicy&& exec, ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator result, BinaryOperation binary_op);

§23.5.2.1.2 back_inserter

template<class Container>
constexpr back_insert_iterator<Container> back_inserter(Container& x);

रिटर्न: back_insert_iterator (x)।

§23.5.2.1 क्लास टेम्पलेट back_insert_iterator

using iterator_category = output_iterator_tag;

तो std::back_inserterके समानांतर संस्करणों के साथ इस्तेमाल नहीं किया जा सकता है std::transform। आउटपुट पुनरावृत्तियों का समर्थन करने वाले संस्करण इनपुट पुनरावृत्तियों के साथ अपने स्रोत से पढ़ते हैं। चूँकि इनपुट पुनरावृत्तियाँ केवल पूर्व और बाद की वृद्धि (.523.3.5.2 इनपुट पुनरावृत्तियों) हो सकती हैं और केवल क्रमिक ( यानी गैर-समानांतर) निष्पादन है, उनके और आउटपुट पुनरावृत्ति के बीच क्रम संरक्षित होना चाहिए।


2
ध्यान दें कि C ++ मानक की ये परिभाषाएँ विशेष प्रकार के एल्गोरिदम के लिए चुने गए एल्गोरिदम के विशेष संस्करण प्रदान करने के लिए कार्यान्वयन से बचती नहीं हैं। उदाहरण के लिए, std::advanceकेवल एक परिभाषा है जो इनपुट-पुनरावृत्तियों को लेती है , लेकिन libstdc ++ द्विदिश-पुनरावृत्तियों और यादृच्छिक-अभिगम-पुनरावृत्तियों के लिए अतिरिक्त संस्करण प्रदान करता है । विशेष संस्करण को पारित किए गए पुनरावृत्ति के प्रकार के आधार पर निष्पादित किया जाता है
डैनियल लैंगर

मुझे नहीं लगता कि आपकी टिप्पणी सही है - ForwardIteratorइसका मतलब यह नहीं है कि आपको चीजों को क्रम में करना है। लेकिन आपने मेरे द्वारा याद की गई चीज़ पर प्रकाश डाला है - समानांतर संस्करणों के लिए जो वे उपयोग ForwardIteratorनहीं करते हैं OutputIterator
टिम्मम

1
आह ठीक है, हाँ मुझे लगता है कि हम सहमत हैं।
टिम्मम्म

1
यह उत्तर यह बताने के लिए कुछ शब्दों को जोड़ने से फायदा हो सकता है कि वास्तव में इसका क्या मतलब है।
बैरी

1
@ बेरी ने कुछ शब्द जोड़े, किसी भी और सभी ने बहुत सराहना की।
पॉल इवांस

0

तो मुझे याद आती है कि समानांतर संस्करण LegacyForwardIteratorएस नहीं लेते हैं LegacyOutputIterator। इसकी प्रतियों को अमान्य किए बिना A को बढ़ाया LegacyForwardIterator जा सकता है, इसलिए आउट ऑफ ऑर्डर समानांतर लागू करने के लिए इसका उपयोग करना आसान है std::transform

मुझे लगता है कि गैर-समानांतर संस्करणों को आदेश में निष्पादित किया std::transform जाना चाहिए। इसके बारे में या तो गलत है या संभवतः मानक सिर्फ इस आवश्यकता को छोड़ता है क्योंकि इसे लागू करने का कोई अन्य तरीका नहीं है। (शॉटगन पता लगाने के लिए मानक के माध्यम से नहीं जा रहा है!)


यदि सभी पुनरावृत्तियाँ पर्याप्त रूप से मजबूत हैं, तो परिवर्तन के गैर समानांतर संस्करण आउट-ऑफ-ऑर्डर निष्पादित कर सकते हैं। उदाहरण में प्रश्न में वे नहीं हैं, ताकि विशेषज्ञता के transformक्रम में होना चाहिए।
कालथ डे

नहीं, वे नहीं कर सकते, क्योंकि LegacyOutputIteratorआप इसे आदेश में उपयोग करने के लिए मजबूर करते हैं।
टिम्मम्म

यह विशेष रूप से std::back_insert_iterator<std::vector<T>>और के लिए अलग से विशेषज्ञ कर सकता है std::vector<T>::iterator। पहले क्रम में होना चाहिए। दूसरे में इस तरह का कोई प्रतिबंध नहीं है
Caleth

आह रुको मैं देख रहा हूं कि आपका क्या मतलब है - यदि आप LegacyForwardIteratorगैर-समानांतर में पास होते हैं transform, तो इसके लिए एक विशेषज्ञता हो सकती है जो इसे क्रम से बाहर करती है। अच्छी बात।
टिम्मम्म

0

मेरा मानना ​​है कि परिवर्तन को आदेश में संसाधित होने की गारंटी हैstd::back_inserter_iteratorएक आउटपुट पुनरावृत्ति है (इसका iterator_categoryसदस्य प्रकार इसके लिए एक उपनाम है std::output_iterator_tag) [back.insert.iterator] के अनुसार ।

नतीजतन, std::transformहै कोई अन्य विकल्प कैसे कॉल सदस्य के लिए की तुलना में अगले चरण के लिए आगे बढ़ने के लिए पर operator++पर resultपैरामीटर।

बेशक, यह केवल निष्पादन नीति के बिना अधिभार के लिए मान्य है, जहां std::back_inserter_iteratorइसका उपयोग नहीं किया जा सकता है (यह अग्रेषण पुनरावृत्ति नहीं है )।


BTW, मैं cppreference के उद्धरणों के साथ बहस नहीं करूंगा। वहाँ के बयान अक्सर अभेद्य या सरलीकृत होते हैं। इन मामलों में, C ++ मानक को देखना बेहतर है। जहां, के संबंध में std::transform, संचालन के आदेश के बारे में कोई उद्धरण नहीं है।


"सी ++ मानक। एसटीडी :: ट्रांसफ़ॉर्म के संबंध में। परिचालनों के क्रम के बारे में कोई उद्धरण नहीं है" चूंकि ऑर्डर का उल्लेख नहीं किया गया है, क्या यह अनिर्दिष्ट नहीं है?
पवित्रब्लैककैट

@HolyBlackCat स्पष्ट रूप से अनिर्दिष्ट, लेकिन आउटपुट पुनरावृत्ति द्वारा लगाया गया। ध्यान दें कि आउटपुट पुनरावृत्तियों के साथ, एक बार जब आप इसे बढ़ाते हैं, तो आप किसी भी पिछले पुनरावृत्ति मूल्य को नहीं मान सकते हैं।
डेनियल लैंगर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.