मैं पैरामीटर पैक विस्तारों को बेहतर ढंग से समझना चाहता था, इसलिए मैंने थोड़ा शोध करने का फैसला किया और, जो मुझे एक बार स्पष्ट लग रहा था, यह समझने की कोशिश करने के बाद कि वास्तव में क्या हो रहा है, इतना स्पष्ट होना बंद हो गया। आइए std::forward के साथ एक मानक पैरामीटर पैक विस्तार की जांच करें:

template <typename... Ts>
void foo(Ts&& ... ts) {
    std::make_tuple(std::forward<Ts>(ts)...);
}

यहां मेरी समझ यह है कि किसी भी पैरामीटर पैक के लिए Ts, std::forward<Ts>(ts)... के परिणामस्वरूप उनके संबंधित प्रकार के साथ अग्रेषित तर्कों की अल्पविराम से अलग सूची होगी, उदाहरण के लिए, ts बराबर 1, 1.0, '1' के लिए , फ़ंक्शन बॉडी का विस्तार किया जाएगा:

std::make_tuple(std::forward<int&&>(1), std::forward<double&&>(1.0), std::forward<char&&>('1'));

और यह मेरे लिए समझ में आता है। फ़ंक्शन कॉल के साथ उपयोग किया जाने वाला पैरामीटर पैक विस्तार, उचित तर्कों के साथ उस फ़ंक्शन पर कॉल की अल्पविराम से अलग सूची में परिणाम देता है।

जो बात मुझे परेशान कर रही है, वह यह है कि अगर हम समान तरीके से कार्यों का एक समूह कॉल करना चाहते हैं, तो हमें कभी-कभी अल्पविराम ऑपरेटर (operator,) की आवश्यकता क्यों पड़ती है? यह उत्तर देखकर, हम इस कोड को पढ़ सकते हैं:

template<typename T>
static void bar(T t) {}

template<typename... Args>
static void foo2(Args... args) {
    (bar(args), ...); // <- notice: comma here
}

int main() {
    foo2(1, 2, 3, "3");
    return 0;    
}

इसके बाद जानकारी दी जाती है कि इसके परिणामस्वरूप निम्नलिखित विस्तार होगा:

(bar(1), bar(2), bar(3), bar("3"));

उचित, समझ में आता है, लेकिन... क्यों? इसके बजाय ऐसा क्यों कर रहे हैं:

template<typename... Args>
static void foo2(Args... args) {
    (bar(args)...); // <- notice: no comma here
}

काम नहीं करता? मेरे तर्क के अनुसार ("फ़ंक्शन कॉल के साथ प्रयुक्त पैरामीटर पैक विस्तार, उचित तर्कों के साथ उस फ़ंक्शन पर कॉल की अल्पविराम से अलग की गई सूची में परिणाम देता है"), इसका विस्तार होना चाहिए:

(bar(1), bar(2), bar(3), bar("3"));

क्या यह bar() के void लौटने के कारण है? ठीक है, bar() को इसमें बदल रहे हैं:

template<typename T>
static int bar(T t) { return 1; }

कुछ नहीं बदलता। मुझे लगता है कि यह सिर्फ 1s की अल्पविराम से अलग की गई सूची में विस्तारित होगा (संभवतः कुछ साइड इफेक्ट कर रहा है, अगर bar() को इस तरह डिजाइन किया गया था)। यह अलग व्यवहार क्यों करता है? मेरा तर्क कहाँ त्रुटिपूर्ण है?

3
Fureeish 9 जिंदा 2020, 03:51

1 उत्तर

सबसे बढ़िया उत्तर

यहां मेरी समझ यह है कि किसी भी पैरामीटर पैक के लिए Ts, std::forward<Ts>(ts)... के परिणामस्वरूप उनके संबंधित प्रकार के साथ अग्रेषित तर्कों की अल्पविराम से अलग की गई सूची होगी

खैर, आपकी समस्या है: ऐसा नहीं है कि यह कैसे काम करता है। या अंत में, बिल्कुल नहीं।

पैरामीटर पैक विस्तार और उनकी प्रकृति जहां उनका उपयोग किया जाता है द्वारा निर्धारित किया जाता है। पैक विस्तार, प्री-सी++17, का उपयोग केवल कुछ व्याकरणिक निर्माणों के भीतर ही किया जा सकता है, जैसे ब्रेसिड-इनिट-सूची या फ़ंक्शन कॉल अभिव्यक्ति। ऐसी संरचनाओं के बाहर (पिछली सूची व्यापक नहीं है), उनके उपयोग की अनुमति नहीं है। सी ++ 17 के बाद, फोल्ड एक्सप्रेशन उन्हें विशिष्ट ऑपरेटरों में उपयोग करने की अनुमति देते हैं।

इसका कारण आंशिक रूप से व्याकरणिक है। इस पर विचार करें: bar(1, (2, 3), 5)। यह फ़ंक्शन को 3 तर्कों के साथ कॉल करता है; व्यंजक (2, 3) एक ही तर्क तक सीमित हो जाता है। अर्थात्, किसी व्यंजक में प्रयुक्त अल्पविराम और फ़ंक्शन कॉल में उपयोग किए जाने वाले मानों के बीच विभाजक के रूप में उपयोग किए जाने वाले अल्पविराम के बीच अंतर होता है। यह अंतर व्याकरणिक स्तर पर किया जाता है; अगर मैं फ़ंक्शन तर्कों के अनुक्रम के बीच में अल्पविराम ऑपरेटर का आह्वान करना चाहता हूं, तो मुझे उस पूरी चीज़ को () में रखना होगा ताकि संकलक अल्पविराम को अल्पविराम अभिव्यक्ति ऑपरेटर के रूप में पहचान सके, अल्पविराम विभाजक नहीं।

गैर-फ़ोल्ड पैक विस्तार पृथक्करण अल्पविराम का उपयोग करने के लिए प्रभावी रूप से विस्तारित होते हैं, न कि अभिव्यक्ति अल्पविराम। जैसे, उन्हें केवल उन जगहों पर विस्तारित किया जा सकता है जहां अलगाव प्रकार का अल्पविराम मान्य है।

(bar(args)...) के काम न करने का कारण यह है कि () व्यंजक दूसरे प्रकार का अल्पविराम नहीं ले सकता।

7
Barry 9 जिंदा 2020, 02:43