C ++ फ़ंक्शन टेम्पलेट आंशिक विशेषज्ञता?


91

मुझे पता है कि नीचे दिया गया कोड एक वर्ग का आंशिक विशेषज्ञता है:

template <typename T1, typename T2> 
class MyClass { 
  … 
}; 


// partial specialization: both template parameters have same type 
template <typename T> 
class MyClass<T,T> { 
  … 
}; 

मुझे यह भी पता है कि C ++ फ़ंक्शन टेम्पलेट को आंशिक विशेषज्ञता नहीं देता है (केवल पूर्ण की अनुमति है)। लेकिन क्या मेरे कोड का मतलब है कि मैंने अपने फ़ंक्शन टेम्पलेट को आंशिक रूप से एक / एक ही प्रकार के तर्कों के लिए विशेष रूप से रखा है? क्योंकि यह Microsoft Visual Studio 2010 एक्सप्रेस के लिए काम करता है! यदि नहीं, तो क्या आप आंशिक विशेषज्ञता अवधारणा की व्याख्या कर सकते हैं?

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

template <typename T1, typename T2> 
inline T1 max (T1 const& a, T2 const& b) 
{ 
    return a < b ? b : a; 
} 

template <typename T> 
inline T const& max (T const& a, T const& b)
{
    return 10;
}


int main ()
{
    cout << max(4,4.2) << endl;
    cout << max(5,5) << endl;
    int z;
    cin>>z;
}

वर्ग विशेषज्ञता के उस सादृश्य को देखें। यदि इसे वर्ग विशेषज्ञता कहा जाता है, तो मुझे फ़ंक्शन को ओवरलोडिंग के समान क्यों माना जाना चाहिए ??
नारेक

1
नहीं, विशेषज्ञता सिंटैक्स अलग है। नीचे दिए गए मेरे उत्तर में (माना) फ़ंक्शन विशेषज्ञता सिंटैक्स देखें।
.

2
यह "अधिकतम के लिए कॉल अस्पष्ट" त्रुटि क्यों नहीं है? कैसे max(5,5)हल करता है max(T const&, T const&) [with T=int]और नहीं करता है max(T1 const&, T2 const&) [with T1=int and T2=int]?
NHDaly

जवाबों:


83

मानक के अनुसार समारोह आंशिक विशेषज्ञता की अनुमति नहीं है। उदाहरण में, आप वास्तव में कार्य को ओवरलोड कर रहे हैं या नहीं कर रहे हैं max<T1,T2>
इसके सिंटैक्स को नीचे की तरह कुछ दिखना चाहिए था, क्या इसकी अनुमति थी:

// Partial specialization is not allowed by the spec, though!
template <typename T> 
inline T const& max<T,T> (T const& a, T const& b)
{                  ^^^^^ <--- [supposed] specializing here
  return 10;
}

फ़ंक्शन टेम्प्लेट के मामले में, केवल पूर्ण विशेषज्ञता सी ++ मानक द्वारा अनुमत है, - संकलक एक्सटेंशन को छोड़कर!


1
@Narek, आंशिक फ़ंक्शन विशेषज्ञता मानक का हिस्सा नहीं है (जो भी कारणों से)। मुझे लगता है कि MSVC एक विस्तार के रूप में इसका समर्थन करता है। कुछ समय बाद हो सकता है, यह अन्य संकलक द्वारा भी अनुमति दी जाएगी।
.मिलिंद

1
@iammilind: कोई बात नहीं। वह पहले से ही जानता है। यही कारण है कि वह कोशिश कर रहा है कि फ़ंक्शन टेम्पलेट के लिए भी। इसलिए मैंने इसे फिर से संपादित किया, जिससे यह स्पष्ट हो गया।
नवाज

22
जो कोई भी समझा सकता है कि आंशिक विशेषज्ञता की अनुमति क्यों नहीं है?
हैलोगूडीबाई

2
@NHDaly, यह अस्पष्टता त्रुटि नहीं देता है क्योंकि 1 फ़ंक्शन अन्य की तुलना में बेहतर मैच है। ऐसा क्यों है का चयन करता है (T, T)से अधिक (T1, T2)के लिए (int, int), क्योंकि पूर्व की गारंटी देता है वहाँ 2 मापदंड हैं और दोनों प्रकार के एक ही हैं कि है, बाद वाला केवल गारंटी देता है कि 2 पैरामीटर हैं। कंपाइलर हमेशा एक सटीक विवरण चुनता है। उदाहरण यदि आपको "नदी" के 2 विवरणों के बीच चयन करना है तो आप किसे चुनेंगे? "पानी का संग्रह" बनाम "बहते हुए पानी का संग्रह"।
Iammilind

1
@kfsone, मुझे लगता है कि यह सुविधा समीक्षा के अधीन है, इसलिए व्याख्या के लिए खुला है। आप इस खुले-एसटीडी अनुभाग का उल्लेख कर सकते हैं , जो मैंने देखा कि C ++ मानक फ़ंक्शन टेम्पलेट को आंशिक विशेषज्ञता की अनुमति क्यों नहीं देता है?
इमिलिंड

44

चूँकि आंशिक विशेषज्ञता की अनुमति नहीं है - जैसा कि अन्य उत्तर में कहा गया है -, आप इसके नीचे std::is_sameऔर इसके std::enable_ifनीचे काम कर सकते हैं:

template <typename T, class F>
inline typename std::enable_if<std::is_same<T, int>::value, void>::type
typed_foo(const F& f) {
    std::cout << ">>> messing with ints! " << f << std::endl;
}

template <typename T, class F>
inline typename std::enable_if<std::is_same<T, float>::value, void>::type
typed_foo(const F& f) {
    std::cout << ">>> messing with floats! " << f << std::endl;
}

int main(int argc, char *argv[]) {
    typed_foo<int>("works");
    typed_foo<float>(2);
}

आउटपुट:

$ ./a.out 
>>> messing with ints! works
>>> messing with floats! 2

संपादित करें : यदि आपको शेष सभी मामलों का इलाज करने में सक्षम होने की आवश्यकता है, तो आप एक परिभाषा जोड़ सकते हैं जिसमें कहा गया है कि पहले से ही इलाज किए गए मामले मेल नहीं खाने चाहिए - अन्यथा आप अस्पष्ट परिभाषाओं में पड़ जाएंगे। परिभाषा हो सकती है:

template <typename T, class F>
inline typename std::enable_if<(not std::is_same<T, int>::value)
    and (not std::is_same<T, float>::value), void>::type
typed_foo(const F& f) {
    std::cout << ">>> messing with unknown stuff! " << f << std::endl;
}

int main(int argc, char *argv[]) {
    typed_foo<int>("works");
    typed_foo<float>(2);
    typed_foo<std::string>("either");
}

जो उत्पादन करता है:

$ ./a.out 
>>> messing with ints! works
>>> messing with floats! 2
>>> messing with unknown stuff! either

यद्यपि यह सभी मामलों में बात थोड़ी उबाऊ लगती है, क्योंकि आपको कंपाइलर को सब कुछ बताना होगा जो आपने पहले ही किया है, यह 5 या कुछ और विशेषज्ञता के साथ व्यवहार करने के लिए काफी उल्लेखनीय है।


वास्तव में ऐसा करने की कोई आवश्यकता नहीं है क्योंकि यह बहुत सरल और स्पष्ट फैशन में फ़ंक्शन ओवरलोडिंग द्वारा नियंत्रित किया जा सकता है।
एड्रियन

2
@ एड्रियन मैं वास्तव में इसे हल करने के लिए किसी अन्य फ़ंक्शन ओवरलोडिंग दृष्टिकोण के बारे में नहीं सोच सकता। आपने देखा कि आंशिक ओवरलोडिंग की अनुमति नहीं है, है ना? हमारे साथ साझा करें अपने समाधान, अगर आपको लगता है कि यह स्पष्ट है।
रुबेन्स

1
वहाँ आसानी से सभी अस्थायी समारोह को पकड़ने के लिए कोई अन्य तरीका है ?
निक

15

विशेषज्ञता क्या है?

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

कार्यात्मक भाषाओं में, पैटर्न मिलान का उपयोग करके चयन किया जाता है :

-- An instance of Maybe is either nothing (None) or something (Just a)
-- where a is any type
data Maybe a = None | Just a

-- declare function isJust, which takes a Maybe
-- and checks whether it's None or Just
isJust :: Maybe a -> Bool

-- definition: two cases (_ is a wildcard)
isJust None = False
isJust Just _ = True

जैसा कि आप देख सकते हैं, हम की परिभाषा को ओवरलोड करते हैं isJust

ठीक है, सी ++ क्लास टेम्पलेट बिल्कुल उसी तरह से काम करते हैं। आप एक मुख्य घोषणा प्रदान करते हैं , जो मापदंडों की संख्या और प्रकृति बताता है। यह सिर्फ एक घोषणा हो सकती है, या एक परिभाषा (आपकी पसंद) के रूप में भी काम करती है, और फिर आप (यदि आप चाहें तो) पैटर्न की विशेषज्ञता प्रदान कर सकते हैं और उन्हें एक अलग से संबद्ध कर सकते हैं (अन्यथा यह मूर्खतापूर्ण होगा) कक्षा का संस्करण ।

टेम्प्लेट फ़ंक्शंस के लिए, विशेषज्ञता कुछ अधिक अजीब है: यह कुछ हद तक अधिभार संकल्प के साथ संघर्ष करता है। जैसे, यह निर्णय लिया गया है कि एक विशेषज्ञता एक गैर-विशिष्ट संस्करण से संबंधित होगी, और अधिभार संकल्प के दौरान विशेषज्ञता पर विचार नहीं किया जाएगा। इसलिए, सही फ़ंक्शन का चयन करने के लिए एल्गोरिथ्म बन जाता है:

  1. नियमित कार्यों और गैर-विशिष्ट टेम्पलेट्स के बीच, अधिभार संकल्प निष्पादित करें
  2. यदि एक गैर-विशिष्ट टेम्पलेट चुना गया है, तो जांचें कि क्या इसके लिए एक विशेषज्ञता मौजूद है जो एक बेहतर मेल होगी

(इन- डीप ट्रीटमेंट के लिए, देखें गोट # 49 )

जैसे, कार्यों का टेम्पलेट विशेषज्ञता एक दूसरे क्षेत्र का नागरिक (शाब्दिक रूप से) है। जहां तक ​​मेरा संबंध है, हम उनके बिना बेहतर होंगे: मुझे अभी तक एक ऐसे मामले का सामना करना पड़ा है जहां एक टेम्पलेट विशेषज्ञता का उपयोग ओवरलोडिंग के साथ हल नहीं किया जा सकता है।

क्या यह एक टेम्पलेट विशेषज्ञता है?

नहीं, यह केवल एक अधिभार है, और यह ठीक है। वास्तव में, ओवरलोड आमतौर पर काम करते हैं जैसा कि हम उनसे उम्मीद करते हैं, जबकि विशेषज्ञ आश्चर्यचकित हो सकते हैं (याद रखें कि गॉटडब्ल्यू लेख मुझे जुड़ा हुआ है)।


"As such, template specialization of functions is a second-zone citizen (literally). As far as I am concerned, we would be better off without them: I have yet to encounter a case where a template specialization use could not be solved with overloading instead."कैसे गैर प्रकार टेम्पलेट मापदंडों के बारे में?
जूल्स जीएम

@ जूलियस: आप अभी भी एक डमी पैरामीटर जैसे कि शुरू करके ओवरलोडिंग का उपयोग कर सकते हैं boost::mpl::integral_c<unsigned, 3u>। एक और समाधान भी उपयोग करने के लिए हो सकता है enable_if/ disable_if, हालांकि यह एक अलग कहानी है।
मैथ्यू एम।

8

गैर-श्रेणी, गैर-परिवर्तनीय आंशिक विशेषज्ञता की अनुमति नहीं है, लेकिन जैसा कि कहा गया है:

कंप्यूटर विज्ञान में सभी समस्याओं को एक अन्य स्तर के अप्रत्यक्ष रूप से हल किया जा सकता है। —— डेविड व्हीलर

फ़ंक्शन कॉल को अग्रेषित करने के लिए एक वर्ग जोड़ना यह हल कर सकता है, यहां एक उदाहरण है:

template <class Tag, class R, class... Ts>
struct enable_fun_partial_spec;

struct fun_tag {};

template <class R, class... Ts>
constexpr R fun(Ts&&... ts) {
  return enable_fun_partial_spec<fun_tag, R, Ts...>::call(
      std::forward<Ts>(ts)...);
}

template <class R, class... Ts>
struct enable_fun_partial_spec<fun_tag, R, Ts...> {
  constexpr static R call(Ts&&... ts) { return {0}; }
};

template <class R, class T>
struct enable_fun_partial_spec<fun_tag, R, T, T> {
  constexpr static R call(T, T) { return {1}; }
};

template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, int> {
  constexpr static R call(int, int) { return {2}; }
};

template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, char> {
  constexpr static R call(int, char) { return {3}; }
};

template <class R, class T2>
struct enable_fun_partial_spec<fun_tag, R, char, T2> {
  constexpr static R call(char, T2) { return {4}; }
};

static_assert(std::is_same_v<decltype(fun<int>(1, 1)), int>, "");
static_assert(fun<int>(1, 1) == 2, "");

static_assert(std::is_same_v<decltype(fun<char>(1, 1)), char>, "");
static_assert(fun<char>(1, 1) == 2, "");

static_assert(std::is_same_v<decltype(fun<long>(1L, 1L)), long>, "");
static_assert(fun<long>(1L, 1L) == 1, "");

static_assert(std::is_same_v<decltype(fun<double>(1L, 1L)), double>, "");
static_assert(fun<double>(1L, 1L) == 1, "");

static_assert(std::is_same_v<decltype(fun<int>(1u, 1)), int>, "");
static_assert(fun<int>(1u, 1) == 0, "");

static_assert(std::is_same_v<decltype(fun<char>(1, 'c')), char>, "");
static_assert(fun<char>(1, 'c') == 3, "");

static_assert(std::is_same_v<decltype(fun<unsigned>('c', 1)), unsigned>, "");
static_assert(fun<unsigned>('c', 1) == 4, "");

static_assert(std::is_same_v<decltype(fun<unsigned>(10.0, 1)), unsigned>, "");
static_assert(fun<unsigned>(10.0, 1) == 0, "");

static_assert(
    std::is_same_v<decltype(fun<double>(1, 2, 3, 'a', "bbb")), double>, "");
static_assert(fun<double>(1, 2, 3, 'a', "bbb") == 0, "");

static_assert(std::is_same_v<decltype(fun<unsigned>()), unsigned>, "");
static_assert(fun<unsigned>() == 0, "");

4

उदाहरण के लिए, आप कानूनी रूप से विशेषज्ञ हो सकते हैं std::swap, लेकिन आप कानूनी रूप से अपने स्वयं के अधिभार को परिभाषित नहीं कर सकते। इसका मतलब है कि आप std::swapअपने स्वयं के कस्टम वर्ग टेम्पलेट के लिए काम नहीं कर सकते ।

ओवरलोडिंग और आंशिक विशेषज्ञता का कुछ मामलों में समान प्रभाव हो सकता है, लेकिन सभी से बहुत दूर।


4
इसलिए आप अपने swapअधिभार को अपने नामस्थान में डालते हैं ।
jpalecek

2

देर से जवाब, लेकिन कुछ देर से पाठकों को यह उपयोगी लग सकता है: कभी-कभी, एक सहायक कार्य - इस तरह से डिज़ाइन किया गया है कि इसे विशेष किया जा सकता है - मुद्दे को भी हल कर सकता है।

तो आइए कल्पना करें, यही हमने हल करने की कोशिश की है:

template <typename R, typename X, typename Y>
void function(X x, Y y)
{
    R* r = new R(x);
    f(r, y); // another template function?
}

// for some reason, we NEED the specialization:
template <typename R, typename Y>
void function<R, int, Y>(int x, Y y) 
{
    // unfortunately, Wrapper has no constructor accepting int:
    Wrapper* w = new Wrapper();
    w->setValue(x);
    f(w, y);
}

ठीक है, आंशिक टेम्प्लेट फंक्शन स्पेशलाइजेशन, हम ऐसा नहीं कर सकते ... तो चलिए एक हेल्पर फंक्शन में स्पेशलाइजेशन के लिए जिस हिस्से को "एक्सपोर्ट" करते हैं, उसको एक्सपर्ट करें और उसका इस्तेमाल करें:

template <typename R, typename T>
R* create(T t)
{
    return new R(t);
}
template <>
Wrapper* create<Wrapper, int>(int n) // fully specialized now -> legal...
{
    Wrapper* w = new Wrapper();
    w->setValue(n);
    return w;
}

template <typename R, typename X, typename Y>
void function(X x, Y y)
{
    R* r = create<R>(x);
    f(r, y); // another template function?
}

यह विशेष रूप से दिलचस्प हो सकता है यदि विकल्प (विशेषज्ञता के बजाय सामान्य अधिभार, रुबेंस द्वारा प्रस्तावित वर्कअराउंड , ... - ऐसा नहीं है कि ये खराब हैं या मेरा बेहतर है, बस एक और ) काफी सामान्य कोड साझा करेंगे।

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