2016-06-28 6 views
7

मैं निम्नलिखित कोड मेरे सवाल को दिखाने के लिए दे: तो फिर मेरे सवालोंमुझे कैसे पता चलेगा कि सी ++ टेम्पलेट एक कंटेनर या एक प्रकार है?

template<T> 
void my_fun(T &obj) 
{ 
    if(obj is a type like float, std::string, double) 
    { 
     perform1() 
    } 
    if(obj is a container like std::vector, std::list) 
    { 
     perform2() 
} 
} 
std::vector<int> abc; 
my_fun(abc); 
int d; 
my_fun(d); 

, मैं कैसे पता कर सकते हैं टेम्पलेट एक सरल प्रकार या एक कंटेनर को संदर्भित करता है? धन्यवाद।

+0

हो जाएगा और मुझे सिफारिश कर आप के बजाय डिजाइन पर देखें, तो आप इस्तेमाल कर सकते हैं [ * प्रकार- लक्षण *] (http://en.cppreference.com/w/cpp/types#Type_traits_.28since_C.2B.2B11.29)। आपको विशिष्ट प्रकार-लक्षणों को लागू करने की आवश्यकता हो सकती है जो आपको उपयुक्त हैं, जैसे 'is_container'। –

+1

'std :: string' निश्चित रूप से वर्णों का एक कंटेनर है, इसलिए यह प्रश्न एक झूठी डिचोटोमी मानता है। – MSalters

उत्तर

2

आपके पास अपने निपटान में कई विकल्प हैं।

  • आप एक डिफ़ॉल्ट व्यवहार चाहते हैं, और यह केवल एक ही प्रकार (या कुछ) के लिए बदलते हैं, तो अपने समारोह के लिए टेम्पलेट विशेषज्ञता का उपयोग करें।

उदाहरण के लिए:

template<typename T> 
void myfunc() { /*default*/ } 


template<> 
void myfunc<int>() { /*specialized version for int */} 
  • आप प्रकार के सामान्य समूहों के लिए अपनी कार्यों के व्यवहार में परिवर्तन करना चाहते हैं, तो आप Type Traits (std::is_fundamental की तरह कुछ) का उपयोग कर सकते हैं। आपको इस प्रकार के अपने खुद के प्रकार के गुणों को लागू करना पड़ सकता है।
+0

ऐसी विशेषता के लिए एक संभावित कार्यान्वयन एक एसएफआईएनएई चेक होगा यदि 'std :: start (std :: declval ()) मान्य है। – HolyBlackCat

1

ए (पैरामीट्रिज्ड) कंटेनर एक प्रकार है। हालांकि, आप यह ओवरलोड कर सकते हैं:

#include <iostream> 
#include <vector> 

template<typename T> 
void my_fun(T &obj) 
{ 
    perform1(); 
} 

template<typename T> 
void my_fun(std::vector<T> &obj) 
{ 
    perform2(); 
} 

int main(void) 
{ 
    int    a; 
    std::vector<int> b; 

    my_fun(a); 
    my_fun(b); 
} 

यदि यह पर्याप्त नहीं है, आप भी std::enable_if<> उपयोग कर सकते हैं ताकि आप उस भाग दो बार लिखने के लिए जरूरत नहीं है।

+1

आप आंशिक रूप से फ़ंक्शन का विशेषज्ञ नहीं हो सकते हैं। आप अपने कोड में क्या कर रहे हैं एक अलग टेम्पलेट्स के साथ समारोह को अधिभारित कर रहा है। – Holt

+0

यह नहीं है कि आप टेम्पलेट का विशेषज्ञ कैसे हैं। – rustyx

+0

यदि आप किसी उत्तर में कोड प्रदान करने जा रहे हैं तो कृपया उचित वाक्यविन्यास के साथ कोड प्रदान करें जो वास्तव में संकलित करता है। –

3

आप अपने खुद के लक्षण और enable_if पर expression SFINAE के माध्यम से एकाधिक अधिभार के साथ लिख सकते हैं। यहाँ एक समाधान void_ttrick का उपयोग करता है (जो संभवतः सी ++ 17 में दिखाई देगा) है:

#include <iostream> 
#include <type_traits> 
#include <vector> 

template<typename ...> 
using to_void = void; // maps everything to void, used in non-evaluated contexts 

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

template<typename T> 
struct is_container<T, 
     to_void<decltype(std::declval<T>().begin()), 
       decltype(std::declval<T>().end()), 
       typename T::value_type 
     >> : std::true_type // will be enabled for iterable objects 
{}; 

template<typename T> 
void f(T param, typename std::enable_if<is_container<T>::value>::type* = nullptr) 
{ 
    std::cout << "Container\n"; 
} 

template<typename T> 
void f(T param, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr) 
{ 
    std::cout << "Fundamental\n"; 
} 

template<typename T> 
void f(T param, 
    typename std::enable_if<!std::is_fundamental<T>::value>::type* = nullptr, 
    typename std::enable_if<!is_container<T>::value>::type* = nullptr) 
{ 
    std::cout << "Other\n"; 
} 

struct Foo{}; 

int main() 
{ 
    int x{};   // fundamental 
    std::vector<int> v; // container 
    Foo s{}; // other 

    f(x); 
    f(v); 
    f(s); 
} 

Live on Coliru

1

my_fun SFINAE का उपयोग कर निम्नलिखित के रूप में लागू किया जा सकता।

namespace details{ 
    struct A{}; 
    struct B:A{}; 

    // A container will have a begin and an end. Also make it first prerference 
    template<typename T> 
    auto my_fun_impl(T const & obj, B *) -> decltype(obj.begin(),obj.end(),void()) 
    { 
     std::cout<<"Container\n"; 
    } 

    // Default choice 
    template<typename T> 
    auto my_fun_impl(T const & obj,A*) -> void 
    { 
     std::cout<<"Other than Container\n"; 
    } 
} 
template<typename T> 
auto my_fun(T const & obj) -> void 
{ 
    details::my_fun_impl(obj,static_cast<details::B *>(0)); 
} 

नोट एक Base या Derived वर्ग सूचक गुजर यहाँ, अन्यथा संकलक के बारे में अस्पष्ट समारोह परिभाषा में शिकायत करेंगे।

कंपाइलर my_fun_impl के B pointer के सटीक हस्ताक्षर से मिलान करने का प्रयास करेगा, यह कंटेनर के मामले में सफल होगा। क्योंकि एक कंटेनर शुरू होता है() और अंत(), पिछला रिटर्न प्रकार में अपेक्षित है।

गैर-कंटेनर प्रकार के मामले में पहला विकल्प मिलान करने में विफल रहेगा। और जैसा कि हम जानते हैं कि Base क्लास पॉइंटर एक व्युत्पन्न क्लास ऑब्जेक्ट रख सकता है, इसलिए डिफ़ॉल्ट मिलान सफल हो जाएगा।

और परीक्षण कोड निम्न में से उत्पादन

int main() 
{ 
    my_fun(std::vector<int>{1,2,3}); 
    my_fun(1); 
} 

एक बुरा डिजाइन की तरह लग रही इसके अलावा

Container 
Other than Container 

Demo on coliru

+0

[एफवाईआई] सभी कंटेनर एक प्रारंभ और अंत सदस्य कार्यों की आपूर्ति नहीं करते हैं। 'std :: queue' मेरे सिर के ऊपर से मेरे पास आता है। – NathanOliver

+0

@NathanOliver, FWIW, 'std :: queue' एक कंटेनर नहीं है, लेकिन एक कंटेनर एडाप्टर है। – chris

+0

@ क्रिसिस आह हाँ। तब मुझे लगता है कि सभी वास्तविक कंटेनर शुरू होते हैं और अंत होते हैं। – NathanOliver

संबंधित मुद्दे