SF ++E को C ++ में कार्य करने के लिए दृष्टिकोण


40

मैं एक प्रोजेक्ट में फ़ंक्शन SFINAE का भारी उपयोग कर रहा हूं और सुनिश्चित नहीं हूं कि निम्नलिखित दो दृष्टिकोणों (शैली के अलावा) के बीच कोई अंतर है:

#include <cstdlib>
#include <type_traits>
#include <iostream>

template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo()
{
    std::cout << "method 1" << std::endl;
}

template <class T, std::enable_if_t<std::is_same_v<T, double>>* = 0>
void foo()
{
    std::cout << "method 2" << std::endl;
}

int main()
{
    foo<int>();
    foo<double>();

    std::cout << "Done...";
    std::getchar();

    return EXIT_SUCCESS;
}

कार्यक्रम आउटपुट उम्मीद के मुताबिक है:

method 1
method 2
Done...

मैंने देखा है कि स्टैकओवरफ़्लो में विधि 2 का अधिक बार उपयोग किया जाता है, लेकिन मुझे विधि 1 पसंद है।

क्या कोई परिस्थिति है जब ये दोनों दृष्टिकोण अलग-अलग हैं?


आप इस कार्यक्रम को कैसे चलाते हैं? यह मेरे लिए संकलन नहीं होगा।
igel

@alter igel को C ++ 17 कंपाइलर की आवश्यकता होगी। मैंने इस उदाहरण का परीक्षण करने के लिए MSVC 2019 का उपयोग किया लेकिन मैं मुख्य रूप से क्लैंग के साथ काम करता हूं।
कीथ

संबंधित: क्यों-मैं-से-बचना-stdenable-if-in-function- हस्ताक्षर और C ++ 20 अवधारणा के साथ नए तरीके भी पेश करता है :-)
Jarod42

@ Jarod42 कॉन्सेप्ट C ++ 20 से मेरे लिए सबसे ज्यादा जरूरी चीजों में से एक है।
वैल का कहना है कि मोनिका

जवाबों:


35

मैंने देखा है कि स्टैकओवरफ़्लो में विधि 2 का अधिक बार उपयोग किया जाता है, लेकिन मुझे विधि 1 पसंद है।

सुझाव: विधि २ को प्राथमिकता दें।

दोनों तरीके एकल कार्यों के साथ काम करते हैं। समस्या तब होती है जब आपके पास एक ही हस्ताक्षर के साथ एक फ़ंक्शन से अधिक है, और आप सेट के केवल एक फ़ंक्शन को सक्षम करना चाहते हैं।

मान लीजिए कि आप सक्षम होना चाहते हैं foo(), संस्करण 1, जब bar<T>()(दिखावा यह एक constexprफ़ंक्शन है) true, और foo(), संस्करण 2, जब bar<T>()है false

साथ में

template <typename T, typename = std::enable_if_t<true == bar<T>()>>
void foo () // version 1
 { }

template <typename T, typename = std::enable_if_t<false == bar<T>()>>
void foo () // version 2
 { }

आपको एक संकलन त्रुटि मिलती है क्योंकि आपके पास एक अस्पष्टता है: एक foo()ही हस्ताक्षर के साथ दो कार्य (एक डिफ़ॉल्ट टेम्पलेट पैरामीटर हस्ताक्षर नहीं बदलता है)।

लेकिन निम्नलिखित समाधान

template <typename T, std::enable_if_t<true == bar<T>(), bool> = true>
void foo () // version 1
 { }

template <typename T, std::enable_if_t<false == bar<T>(), bool> = true>
void foo () // version 2
 { }

काम करता है, क्योंकि SFINAE कार्यों के हस्ताक्षर को संशोधित करता है।

असंबंधित अवलोकन: एक तीसरी विधि भी है: वापसी प्रकार को सक्षम / अक्षम करें (वर्ग / संरचना निर्माणकर्ताओं को छोड़कर, जाहिर है)

template <typename T>
std::enable_if_t<true == bar<T>()> foo () // version 1
 { }

template <typename T>
std::enable_if_t<false == bar<T>()> foo () // version 2
 { }

विधि 2 के रूप में, विधि 3 समान हस्ताक्षर के साथ वैकल्पिक कार्यों के चयन के साथ संगत है।


1
महान विवरण के लिए धन्यवाद, मैं तरीकों 2 और 3 को अब से पसंद करूंगा :-)
कीथ

"एक डिफ़ॉल्ट टेम्पलेट पैरामीटर हस्ताक्षर नहीं बदलता" - यह आपके दूसरे संस्करण में कैसे भिन्न है, जो डिफ़ॉल्ट टेम्पलेट पैरामीटर का भी उपयोग करता है?
एरिक

1
@ एरिक - कहने के लिए गैर सरल ... मुझे लगता है कि अन्य उत्तर यह बेहतर समझाते हैं ... यदि SFINAE डिफ़ॉल्ट टेम्पलेट तर्क को सक्षम / अक्षम करता है, तो foo()जब आप इसे एक स्पष्ट दूसरे टेम्पलेट पैरामीटर ( foo<double, double>();कॉल) के साथ कहते हैं , तो फ़ंक्शन उपलब्ध रहता है । और अगर उपलब्ध रहता है, तो दूसरे संस्करण के साथ अस्पष्टता है। विधि 2 के साथ, SFINAE दूसरे तर्क को सक्षम / अक्षम करता है, डिफ़ॉल्ट पैरामीटर को नहीं। इसलिए आप इसे पैरामीटर की खोज नहीं कह सकते क्योंकि एक प्रतिस्थापन विफलता है जो दूसरे पैरामीटर की अनुमति नहीं देती है। इसलिए संस्करण उपलब्ध नहीं है, इसलिए कोई अस्पष्टता नहीं है
अधिकतम ६६

3
विधि 3 में आमतौर पर प्रतीक-नाम में लीक नहीं होने का अतिरिक्त लाभ है। auto foo() -> std::enable_if_t<...>फ़ंक्शन-हस्ताक्षर को छिपाने और फ़ंक्शन-तर्कों का उपयोग करने की अनुमति देने के लिए संस्करण अक्सर उपयोगी होता है।
डिडुप्लिकेटर

@ max66: इसलिए महत्वपूर्ण बिंदु यह है कि यदि पैरामीटर की आपूर्ति की जाती है और कोई डिफ़ॉल्ट की आवश्यकता नहीं है, तो टेम्पलेट पैरामीटर डिफ़ॉल्ट में प्रतिस्थापन विफलता एक त्रुटि नहीं है?
एरिक

21

अधिकतम 66 के जवाब के अलावा , विधि 2 को पसंद करने का एक और कारण यह है कि विधि 1 के साथ, आप (गलती से) एक स्पष्ट प्रकार के पैरामीटर को दूसरे टेम्पलेट तर्क के रूप में पास कर सकते हैं और SFINAE तंत्र को पूरी तरह से हरा सकते हैं। यह टाइपो, कॉपी / पेस्ट त्रुटि, या बड़े टेम्प्लेट तंत्र में एक निरीक्षण के रूप में हो सकता है।

#include <cstdlib>
#include <type_traits>
#include <iostream>

// NOTE: foo should only accept T=int
template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo(){
    std::cout << "method 1" << std::endl;
}

int main(){

    // works fine
    foo<int>();

    // ERROR: subsitution failure, as expected
    // foo<double>();

    // Oops! also works, even though T != int :(
    foo<double, double>();

    return 0;
}

यहां लाइव डेमो


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