फ़ंक्शन टेम्पलेट के अंदर फ़ंक्शन निष्पादित करें केवल उन प्रकारों के लिए जो फ़ंक्शन परिभाषित हैं


13

मेरे पास एक फ़ंक्शन टेम्प्लेट है जो इनपुट के रूप में कई अलग-अलग प्रकार लेता है। उन प्रकारों में से उनमें से केवल एक का ही getInt()कार्य है। इसलिए मैं चाहता हूं कि कोड केवल उस प्रकार के लिए फ़ंक्शन को चलाए। कृपया कोई उपाय सुझाएं। धन्यवाद

#include <type_traits>
#include <typeinfo>

class X {
    public:
    int getInt(){
        return 9;
    }
};

class Y{

};

template<typename T>
void f(T& v){
    // error: 'class Y' has no member named 'getInt'
    // also tried std::is_same<T, X>::value 
    if(typeid(T).name() == typeid(X).name()){
        int i = v.getInt();// I want this to be called for X only
    }
}

int main(){
    Y y;
    f(y);
}

आपकी समस्या से संबंधित नहीं है, लेकिन type_infoसंरचना में एक समानता तुलना ऑपरेटर है , इसलिए typeid(T) == typeid(X)इसे भी काम करना चाहिए।
कुछ प्रोग्रामर ने

5
का प्रयोग करें: if constexprशर्त के साथ is_same_v<T,X>
rafix07

इस का समाधान आधिकारिक रूप से इस वर्ष बाद में अवधारणाओं के साथ और अधिक सुरुचिपूर्ण हो जाएगा। अभी सुपर सहायक नहीं है, मुझे पता है।
निचोड़

आपकी समस्या को हल करने के कई तरीके हैं। ऊपर उल्लेख किया गया एक युगल। आप यह देखने के लिए भी भिन्न प्रकार के लक्षणों का उपयोग कर सकते हैं कि क्या किसी प्रकार के कॉल करने योग्य getIntसदस्य हैं। अकेले स्टैकओवरफ्लो डॉट कॉम पर यहाँ कुछ प्रश्न होने चाहिए, यह देखने के लिए कि कैसे एक संरचना या वर्ग में एक विशिष्ट सदस्य फ़ंक्शन है, यदि आप बस थोड़ा खोज करते हैं।
कुछ प्रोग्रामर ने

जवाबों:


10

यदि आप fसभी प्रकार के फ़ंक्शन को कॉल करने में सक्षम होना चाहते हैं getInt, जिसमें फ़ंक्शन सदस्य हैं , तो बस नहीं X, आप फ़ंक्शन के लिए 2 ओवरलोड की घोषणा कर सकते हैं f:

  1. उन प्रकारों के लिए getIntजिनमें कक्षा सहित सदस्य कार्य होते हैंX

  2. कक्षा सहित अन्य सभी प्रकारों के लिए Y

सी ++ 11 / सी ++ 17 समाधान

ध्यान में रखते हुए, आप ऐसा कुछ कर सकते हैं:

#include <iostream>
#include <type_traits>

template <typename, typename = void>
struct has_getInt : std::false_type {};

template <typename T>
struct has_getInt<T, std::void_t<decltype(((T*)nullptr)->getInt())>> : std::is_convertible<decltype(((T*)nullptr)->getInt()), int>
{};

class X {
public:
    int getInt(){
        return 9;
    }
};

class Y {};

template <typename T,
          typename std::enable_if<!has_getInt<T>::value, T>::type* = nullptr>
void f(T& v) {
    // only for Y
    std::cout << "Y" << std::endl;
}

template <typename T,
          typename std::enable_if<has_getInt<T>::value, T>::type* = nullptr>
void f(T& v){
    // only for X
    int i = v.getInt();
    std::cout << "X" << std::endl;
}

int main() {
    X x;
    f(x);

    Y y;
    f(y);
}

इसे लाइव देखें

कृपया ध्यान दें कि std::void_tC ++ 17 में पेश किया गया है, लेकिन यदि आप C ++ 11 तक सीमित हैं, तो void_tअपने दम पर लागू करना वास्तव में आसान है :

template <typename...>
using void_t = void;

और यहाँ C ++ 11 संस्करण लाइव है

C ++ 20 में हमारे पास क्या है?

C ++ 20 बहुत सारी अच्छी चीजें लाता है और उनमें से एक अवधारणा है । उपरोक्त चीज़ जो C ++ 11 / C ++ 14 / C ++ 17 के लिए मान्य है, C ++ 20 में काफी कम की जा सकती है:

#include <iostream>
#include <concepts>

template<typename T>
concept HasGetInt = requires (T& v) { { v.getInt() } -> std::convertible_to<int>; };

class X {
public:
    int getInt(){
        return 9;
    }
};

class Y {};

template <typename T>
void f(T& v) {
    // only for Y
    std::cout << "Y" << std::endl;
}

template <HasGetInt T>
void f(T& v){
    // only for X
    int i = v.getInt();
    std::cout << "X" << std::endl;
}

int main() {
    X x;
    f(x);

    Y y;
    f(y);
}

इसे लाइव देखें


C ++ 17 से पहले, void_tकुछ पुराने संकलक के मुद्दों के कार्यान्वयन के कारण (जैसा कि लिंक द्वारा बताया गया है)।
Jarod42

यह दो अधिभार लिखने के लिए कड़ाई से आवश्यक नहीं है ("की जरूरत है" के साथ "कर सकते हैं" बहुत बेहतर imho होगा)
idclev 463035818

@ idclev463035818 अपडेट किया गया। धन्यवाद
सरौता

1
@SSAnne अपडेट किया गया
NutCracker

1
अवधारणा की परिभाषा सटीक नहीं है। आप एक इंट को परिणाम दे रहे हैं ताकि अवधारणा होनी चाहिएtemplate<typename T> concept HasGetInt = requires (T& v) { {v.getInt()} -> std::convertible_to<int>; };
हुई

8

आप if constexprC ++ 17 से उपयोग कर सकते हैं :

template<typename T>
void f(T& v){
    if constexpr(std::is_same_v<T, X>) { // Or better create trait has_getInt
        int i = v.getInt();// I want this to be called for X only
    }
    // ...
}

इससे पहले, आपको ओवरलोड और SFINAE या टैग प्रेषण का उपयोग करना होगा।


if constexprएक C ++ 17 फीचर है।
एंड्री सेमशेव

हालाँकि, यह केवल क्लास के लिए काम करेगाX
NutCracker

प्रश्न अब केवल C ++ 11 / C ++ 14
NutCracker

@NutCracker: टैग / प्रश्न को अपडेट करने और मौजूदा उत्तरों को अमान्य करने के लिए अच्छा नहीं है ... (भले ही इसके बारे में चेतावनी ठीक है)।
Jarod42

मैं सिर्फ टैग को अपडेट करता हूं ... प्रश्न शीर्षक ओपी
न्यूट्रैकर

7

इसे सरल और अधिभार रखें। कम से कम C ++ 98 के बाद से काम किया है ...

template<typename T>
void f(T& v)
{
    // do whatever
}

void f(X& v)
{
    int result = v.getInt();
}

यह पर्याप्त है अगर getIntफ़ंक्शन के साथ केवल एक प्रकार है। यदि अधिक है, तो यह अब और सरल नहीं है। इसे करने के कई तरीके हैं, यहाँ एक है:

struct PriorityA { };
struct PriorityB : PriorityA { };

template<typename T>
void f_impl(T& t, PriorityA)
{
    // generic version
}

// use expression SFINAE (-> decltype part)
// to enable/disable this overload
template<typename T>
auto f_impl(T& t, PriorityB) -> decltype(t.getInt(), void())
{
    t.getInt();
}

template<typename T>
void f(T& t)
{
    f_impl(t, PriorityB{ } ); // this will select PriorityB overload if it exists in overload set
                              // otherwise PriorityB gets sliced to PriorityA and calls generic version
}

डायग्नोस्टिक आउटपुट के साथ लाइव उदाहरण।


1
इस मामले में यह काम करेगा क्योंकि केवल एक अधिभार (के लिए X) है, लेकिन, अगर getIntभविष्य में सदस्य के साथ इसी तरह के अधिक प्रकार थे, तो यह इतना अच्छा अभ्यास नहीं है। आप शायद ध्यान देना चाहते हैं कि
NutCracker

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