c++ - Same_as अवधारणा दो बार टाइप समानता की जाँच क्यों करती है?




c++20 concept (2)

दिलचस्प सवाल। मैंने हाल ही में एंड्रयू सटन की बातचीत को कॉन्सेप्ट पर देखा है, और प्रश्नोत्तर सत्र में किसी ने निम्नलिखित प्रश्न पूछा है: (निम्न लिंक में टाइमस्टैम्प): CppCon 2018: एंड्रयू सटन "60 में अवधारणाएं: सब कुछ आपको जानना चाहिए और कुछ भी नहीं जिसे आप नहीं चाहते"

इसलिए यह सवाल उबलता है: If I have a concept that says A && B && C, another says C && B && A, would those be equivalent? एंड्रयू ने हां में उत्तर दिया, लेकिन इस तथ्य को इंगित किया कि संकलक में कुछ आंतरिक विधियां (जो उपयोगकर्ता के लिए पारदर्शी हैं) अवधारणाओं को परमाणु तार्किक प्रस्तावों ( atomic constraints शब्द के रूप में atomic constraints ) को विघटित करने के लिए कहते हैं और जांचें कि क्या वे समकक्ष हैं।

अब देखें कि cppreference std::same_as बारे में क्या कहता है std::same_as :

std::same_as<T, U> subsumes std::same_as<U, T> और इसके विपरीत।

यह मूल रूप से "इफ-एंड-ओनली-इफ" रिश्ता है: वे एक-दूसरे को प्रभावित करते हैं। (तार्किक समानता)

मेरा अनुमान है कि यहाँ परमाणु अड़चनें std::is_same_v<T, U> । जिस तरह से कंपाइलर std::is_same_v इलाज करते हैं std::is_same_v उन्हें लगता है कि std::is_same_v<T, U> और std::is_same_v<U, T> दो अलग-अलग बाधाओं के रूप में हैं (वे अलग-अलग संस्थाएं हैं!)। इसलिए यदि आप std::same_as लागू करते हैं std::same_as उनमें से केवल एक का उपयोग कर:

template< class T, class U >
concept same_as = detail::SameHelper<T, U>;

तब std::same_as<T, U> और std::same_as<U, T> विभिन्न परमाणु अवरोधों के लिए "विस्फोट" करेंगे और समतुल्य नहीं बनेंगे।

खैर, संकलक देखभाल क्यों करता है?

इस उदाहरण पर विचार करें :

#include <type_traits>
#include <iostream>
#include <concepts>

template< class T, class U >
concept SameHelper = std::is_same_v<T, U>;

template< class T, class U >
concept my_same_as = SameHelper<T, U>;

// template< class T, class U >
// concept my_same_as = SameHelper<T, U> && SameHelper<U, T>;

template< class T, class U> requires my_same_as<U, T>
void foo(T a, U b) {
    std::cout << "Not integral" << std::endl;
}

template< class T, class U> requires (my_same_as<T, U> && std::integral<T>)
void foo(T a, U b) {
    std::cout << "Integral" << std::endl;
}

int main() {
    foo(1, 2);
    return 0;
}

आदर्श रूप से, my_same_as<T, U> && std::integral<T> my_same_as<U, T> ; इसलिए, कंपाइलर को दूसरे टेम्प्लेट स्पेशलाइजेशन का चयन करना चाहिए, सिवाय इसके ... यह नहीं: कंपाइलर एक त्रुटि error: call of overloaded 'foo(int, int)' is ambiguous उत्सर्जन करता है error: call of overloaded 'foo(int, int)' is ambiguous

इसके पीछे कारण यह है कि चूंकि my_same_as<U, T> और my_same_as<T, U> एक-दूसरे को नहीं my_same_as<T, U> && std::integral<T> , my_same_as<T, U> && std::integral<T> और my_same_as<U, T> अतुलनीय हो गए हैं () आंशिक रूप से आदेश दिया गया निर्वाह के संबंध के तहत बाधाओं का सेट)।

हालाँकि, यदि आप प्रतिस्थापित करते हैं

template< class T, class U >
concept my_same_as = SameHelper<T, U>;

साथ में

template< class T, class U >
concept my_same_as = SameHelper<T, U> && SameHelper<U, T>;

कोड संकलित करता है।

https://en.cppreference.com/w/cpp/concepts/same_as पर समान_स अवधारणा के संभावित कार्यान्वयन को देखते हुए मैंने देखा कि कुछ अजीब हो रहा है।

namespace detail {
    template< class T, class U >
    concept SameHelper = std::is_same_v<T, U>;
}

template< class T, class U >
concept same_as = detail::SameHelper<T, U> && detail::SameHelper<U, T>;

पहला सवाल यह है कि एक SameHelper अवधारणा को क्यों किया जाता है? दूसरी बात यह है कि यदि T और U के समान ही है तो same_as समान है? क्या यह बेमानी नहीं है?


std::is_same को सही के रूप में परिभाषित किया जाता है यदि और केवल यदि:

T और U समान cv-योग्यता के साथ एक ही प्रकार का नाम देते हैं

जहां तक ​​मुझे पता है, मानक "एक ही प्रकार" के अर्थ को परिभाषित नहीं करता है, लेकिन प्राकृतिक भाषा और तर्क में "समान" एक समतुल्य संबंध है और इस प्रकार यह सराहनीय है।

इस धारणा को देखते हुए, जिसे मैं is_same_v<T, U> && is_same_v<U, V> , is_same_v<T, U> && is_same_v<U, V> वास्तव में बेमानी होगा। लेकिन same_as संदर्भ में is_same_v निर्दिष्ट नहीं है; यह केवल प्रदर्शनी के लिए है।

दोनों के लिए स्पष्ट जांच same-as-impl same_as लिए कार्यान्वयन की अनुमति देती है ताकि same_as बिना same_as को संतुष्ट किया जा सके। इसे इस तरह से निर्दिष्ट करना वास्तव में वर्णन करता है कि यह अवधारणा कैसे प्रतिबंधित किए बिना व्यवहार करती है कि इसे कैसे लागू किया जा सकता है।

वास्तव में इस दृष्टिकोण को is_same_v संदर्भ में निर्दिष्ट करने के बजाय क्यों चुना गया, मुझे नहीं पता। चुने हुए दृष्टिकोण का एक फायदा यकीनन यह है कि दो परिभाषाएं एक-दूसरे से जुड़ी हुई हैं। एक दूसरे पर निर्भर नहीं है।





concept