टेम्पलेट क्लास में C ++ 20 आउट-ऑफ-क्लास परिभाषा


12

C ++ के C + 20 मानक तक, जब हम एक आउट-ऑफ-क्लास ऑपरेटर को परिभाषित करना चाहते थे जो टेम्पलेट क्लास के कुछ निजी सदस्यों का उपयोग करता है, तो हम इसके समान एक निर्माण का उपयोग करेंगे:

template <typename T>
class Foo;

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs);

template <typename T>
class Foo {
public:
    constexpr Foo(T k) : mK(k) {}

    constexpr friend bool operator==<T>(T lhs, const Foo& rhs);

private:
    T mK;
};

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
    return lhs == rhs.mK;
}

int main() {
    return 1 == Foo<int>(1) ? 0 : 1;
}

C ++ 20 के बाद से, हम आउट-ऑफ-क्लास घोषणा को छोड़ सकते हैं, इस प्रकार आगे की घोषणा भी, इसलिए हम बस के साथ दूर हो सकते हैं:

template <typename T>
class Foo {
public:
    constexpr Foo(T k) : mK(k) {}

    constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
    T mK;
};

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
    return lhs == rhs.mK;
}

Demo

अब, मेरा प्रश्न यह है कि C ++ 20 का कौन सा भाग हमें ऐसा करने की अनुमति देता है? और यह पहले के सी ++ मानकों में क्यों संभव नहीं था?


जैसा कि टिप्पणियों में बताया गया था, क्लैंग डेमो में प्रस्तुत इस कोड को स्वीकार नहीं करता है, जो बताता है कि यह वास्तव में जीसीसी में बग हो सकता है।

मैंने gcc के बगज़िला पर एक बग रिपोर्ट दर्ज की


2
मैं व्यक्तिगत रूप से वर्ग परिभाषा में पसंद करता हूं, टेम्पलेट फ़ंक्शन (और कटौती "मुद्दों" (कोई मैच नहीं "c string" == Foo<std::string>("foo"))) से परहेज करता हूं ।
Jarod42

@ Jarod42 मैं पूरी तरह सहमत हूं, मैं इन-क्लास परिभाषा भी पसंद करता हूं। मुझे यह जानकर आश्चर्य हुआ कि C ++ 20 हमें ouf-of-class को परिभाषित करते समय फ़ंक्शन हस्ताक्षर को तीन बार दोहराने की अनुमति नहीं देता है, जो सार्वजनिक एपीआई में उपयोगी हो सकता है जहां कार्यान्वयन एक छिपी हुई फ़ाइल में है।
ProXicT

मैंने ध्यान नहीं दिया कि यह असंभव था। कैसे मैं इसे इस तरह से मुद्दों के बिना दूर इस्तेमाल किया है?
ALX23z

1
हममम, में temp.friend , ज्यादा नहीं बदला है, खासकर नहीं 1.3 जो इस व्यवहार के लिए जिम्मेदार होना चाहिए। चूंकि क्लैंग आपके कोड को स्वीकार नहीं करता है , इसलिए मैं बग होने की वजह से झुक रहा हूं।
n314159

@ ALX23z यह वर्ग के आउट-ऑफ-क्लास घोषणा के बिना काम करता है यदि वर्ग को विस्थापित नहीं किया जाता है।
ProXicT

जवाबों:


2

जीसीसी में एक बग है।

नाम लुकअप हमेशा एक से पहले प्रदर्शित होने वाले टेम्पलेट नामों के लिए किया जाता है <, यहां तक ​​कि जब प्रश्न में नाम एक (मित्र, स्पष्ट विशेषज्ञता, या स्पष्ट तात्कालिकता) घोषणा में घोषित किया जा रहा है।

क्योंकि operator==मित्र घोषणा में नाम एक अयोग्य नाम है और एक टेम्प्लेट में नाम देखने के अधीन है, दो-चरण नाम लुकअप नाम लागू होते हैं। इस संदर्भ में, operator==एक आश्रित नाम नहीं है (यह एक फ़ंक्शन कॉल का हिस्सा नहीं है, इसलिए ADL लागू नहीं होता है), इसलिए नाम को उस बिंदु पर देखा और बाध्य किया जाता है जहां यह प्रकट होता है (देखें [temp.nondep] पैराग्राफ 1)। आपका उदाहरण बीमार है क्योंकि यह नाम लुकअप की कोई घोषणा नहीं है operator==

मुझे उम्मीद है कि P0846R0 के कारण GCC C ++ 20 मोड में इसे स्वीकार कर रहा है , जो कि (उदाहरण के लिए) operator==<T>(a, b)एक टेम्पलेट में उपयोग किए जाने की अनुमति देता है, भले ही कोई operator==टेम्पलेट के रूप में कोई पूर्व घोषणा दिखाई न दे।

यहाँ एक और भी दिलचस्प टेस्टकेस है:

template <typename T> struct Foo;

#ifdef WRONG_DECL
template <typename T> bool operator==(Foo<T> lhs, int); // #1
#endif

template <typename T> struct Foo {
  friend bool operator==<T>(Foo<T> lhs, float); // #2
};

template <typename T> bool operator==(Foo<T> lhs, float); // #3
Foo<int> f;

साथ -DWRONG_DECL, # 2 दोस्त घोषणा के लिए अयोग्य देखने टेम्पलेट परिभाषा के संदर्भ में, पाता है # 1 घोषणा, जिनमें से instantiated दोस्त से मेल नहीं खाता:, जीसीसी और बजना का मानना है कि इस कार्यक्रम के बीमार का गठन है Foo<int>। घोषणा # 3 भी नहीं माना जाता है, क्योंकि टेम्पलेट में अयोग्य लुक इसे नहीं ढूंढता है।

-UWRONG_DECLजीसीसी के साथ (C ++ 17 और पूर्व में) और क्लैंग इस बात से सहमत हैं कि यह कार्यक्रम एक अलग कारण से बीमार है: operator==लाइन # 2 के लिए अयोग्य लुकअप को कुछ नहीं मिलता है।

लेकिन -UWRONG_DECL, C ++ 20 मोड में GCC यह तय करता प्रतीत होता है कि यह ठीक है कि operator==# 2 में अयोग्य लुकअप विफल हो जाता है (संभवतः P0846R0 के कारण), और फिर टेम्प्लेट तात्कालिकता संदर्भ से लुक को फिर से करना प्रतीत होता है, अब # 3 ढूंढ रहा है, टेम्पलेट्स के लिए सामान्य दो-चरण नाम लुकअप नियम का उल्लंघन।


इस विस्तृत विवरण के लिए धन्यवाद, बहुत अच्छी तरह से रखा गया!
21
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.