फ़ंक्शन टेम्प्लेट के लिए डिफ़ॉल्ट टेम्पलेट तर्क


187

डिफ़ॉल्ट टेम्प्लेट तर्कों को केवल क्लास टेम्प्लेट पर ही अनुमति क्यों दी जाती है? हम सदस्य फ़ंक्शन टेम्पलेट में डिफ़ॉल्ट प्रकार को परिभाषित क्यों नहीं कर सकते? उदाहरण के लिए:

struct mycclass {
  template<class T=int>
  void mymember(T* vec) {
    // ...
  }
};

इसके बजाय, C ++ मजबूर करता है कि डिफ़ॉल्ट टेम्पलेट तर्क केवल एक क्लास टेम्पलेट पर अनुमत हैं।


8
+1 यह वाकई एक पेचीदा सवाल है।
आरा

1
पहले तीन पोस्ट किए गए उत्तरों के लिए, इस उदाहरण पर विचार करें: struct S { template <class R = int> R get_me_R() { return R(); } };टेम्प्लेट पैरामीटर को संदर्भ से घटाया नहीं जा सकता।
आरा

3
अच्छा प्रश्न। 3 लोगों ने पहले ही यह कहने का उत्तर दिया है कि "इसका कोई मतलब नहीं है", और वे सभी सामान्य रूप से गलत हैं। फंक्शन टेम्प्लेट पैरामीटर हमेशा फ़ंक्शन कॉल मापदंडों से कटौती योग्य नहीं होते हैं । उदाहरण के लिए, यदि उन्हें अनुमति दी गई है तो मैं लिख सकता हूं template <int N = 1> int &increment(int &i) { i += N; return i; }, और फिर increment(i);या increment<2>(i);। जैसे भी हो, मुझे लिखना होगा increment<1>(i);
स्टीव जेसप

दरअसल, मेरा और आरा का उदाहरण दोनों को ओवरलोडिंग से निपटा जा सकता है। लिट की नहीं, मुझे लगता है, क्योंकि टेम्प्लेट पैरामीटर घटाया जा सकता है या निर्दिष्ट किया जा सकता है।
स्टीव जेसोप

3
@Steve: लापता अर्धविराम वास्तव में 1 अप्रैल, 1992 के जर्नल ऑफ ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग में प्रकाशित बी। स्टैवरुप के "ओवरलोडिंग ऑफ सी ++ व्हॉट्सस्पेस" के पूरक के लिए एक नया ईओएल ऑपरेटर अधिभार है। ( www2.research.att.com/~bs/ paper.html )

जवाबों:


148

यह डिफ़ॉल्ट टेम्पलेट तर्क देने के लिए समझ में आता है। उदाहरण के लिए आप एक प्रकार का कार्य बना सकते हैं:

template<typename Iterator, 
         typename Comp = std::less<
            typename std::iterator_traits<Iterator>::value_type> >
void sort(Iterator beg, Iterator end, Comp c = Comp()) {
  ...
}

C ++ 0x उन्हें C ++ से परिचित कराता है। बज़्ने स्ट्रॉस्ट्रप द्वारा यह दोष रिपोर्ट देखें: फ़ंक्शन टेम्प्लेट के लिए डिफ़ॉल्ट टेम्पलेट तर्क और वह क्या कहता है

फ़ंक्शन टेम्प्लेट के लिए डिफ़ॉल्ट टेम्प्लेट तर्कों का निषेध उस समय का एक गलत विवरण है, जहां फ़्रीस्टैंडिंग फ़ंक्शंस को द्वितीय श्रेणी के नागरिकों के रूप में माना गया था और सभी टेम्पलेट तर्कों को निर्दिष्ट के बजाय फ़ंक्शन तर्कों से कटौती करने की आवश्यकता थी।

अनावश्यक रूप से फ्रीस्टैंडिंग कार्यों को सदस्य कार्यों से अलग करके प्रोग्रामिंग शैली को गंभीर रूप से प्रतिबंधित कर देता है, इस प्रकार एसटीएल-शैली कोड लिखना कठिन हो जाता है।


@ अरमान, दोष रिपोर्ट लिंक में C ++ 0x और चर्चा के लिए कार्य मसौदे में किए गए बदलाव शामिल हैं। डिफ़ॉल्ट तर्कों से न तो कटौती की गई और न ही स्पष्ट रूप से निर्दिष्ट की गई। GCC4.4 C ++ 0x मोड में फ़ंक्शन टेम्प्लेट के लिए डिफ़ॉल्ट तर्कों का समर्थन करता है।
जोहान्स शाउब - १५'१०

4
सवाल या जवाब के साथ कुछ नहीं करना है, लेकिन हर्ब सटर ने पिछले शनिवार की बैठक के बाद upcomming मानक C ++ 11 कहा। मैं अभी इसे पढ़ता हूं और इसे साझा करने का अनुभव करता हूं
डेविड रॉड्रिग्ज़ - dribeas

और अनिवार्य सवाल का पालन करें ... जब यह अन्य संकलक में रास्ता बनाने की उम्मीद है :)
जेमी कुक

@ जोहान्सचैब-लिटब मैं एक ही समस्या थी: एक टेम्पलेट फ़ंक्शन में डिफ़ॉल्ट प्रकार को मसालेदार करने की कोई संभावना नहीं है। मैंने डिफ़ॉल्ट प्रकार ( doubleमेरे मामले में) पर फ़ंक्शन के एक स्पष्ट तात्कालिकता द्वारा हल किया है । शायद यह "सामान्य" नहीं है, लेकिन क्या इस अभ्यास के साथ कोई कमी है? धन्यवाद।
जैकोलेंटर

निम्न कोड, संकलन करने में विफल रहता है, जैसे कि error: invalid conversion from ‘int’ to ‘int*’, किसी भी विचार के कारण: `#include <array> #include <एल्गोरिथम> #include <कार्यात्मक> टेम्पलेट <टाइपनेम Iterator, टाइपनेमame Comp = std :: less" Iterator>> void my_sort ( Iterator beg, Iterator end, Comp c = Comp ()) {std :: सॉर्ट (भीख, अंत, c); } int main () {std :: array <int, 5> ar {5,2,21,7,4}; my_sort (ar.begin (), ar.end ()); } `
ल्यूक पीटरसन

36

C ++ टेम्प्लेट्स को उद्धृत करने के लिए : पूर्ण गाइड (पृष्ठ 207):

जब टेम्पलेट मूल रूप से C ++ भाषा में जोड़े गए थे, तो स्पष्ट फ़ंक्शन टेम्पलेट तर्क एक वैध निर्माण नहीं थे। फंक्शन टेम्प्लेट तर्कों को हमेशा कॉल एक्सप्रेशन से कमिटेड होना पड़ता था। नतीजतन, डिफ़ॉल्ट फ़ंक्शन टेम्पलेट तर्कों को अनुमति देने के लिए कोई सम्मोहक कारण नहीं लग रहा था क्योंकि डिफ़ॉल्ट मान हमेशा कटौती मूल्य से ओवरराइड होगा।


सरल और संक्षिप्त :)
इनक्यूसिटिव

17

अब तक, फ़ंक्शन टेम्प्लेट के लिए डिफ़ॉल्ट टेम्प्लेट मापदंडों के सभी प्रोफार्मा उदाहरण ओवरलोड के साथ किए जा सकते हैं।

अरक:

struct S { 
    template <class R = int> R get_me_R() { return R(); } 
};

हो सकता है:

struct S {
    template <class R> R get_me_R() { return R(); } 
    int get_me_R() { return int(); }
};

मेरा अपना:

template <int N = 1> int &increment(int &i) { i += N; return i; }

हो सकता है:

template <int N> int &increment(int &i) { i += N; return i; }
int &increment(int &i) { return increment<1>(i); }

litb:

template<typename Iterator, typename Comp = std::less<Iterator> >
void sort(Iterator beg, Iterator end, Comp c = Comp())

हो सकता है:

template<typename Iterator>
void sort(Iterator beg, Iterator end, std::less<Iterator> c = std::less<Iterator>())

template<typename Iterator, typename Comp >
void sort(Iterator beg, Iterator end, Comp c = Comp())

Stroustrup:

template <class T, class U = double>
void f(T t = 0, U u = 0);

हो सकता है:

template <typename S, typename T> void f(S s = 0, T t = 0);
template <typename S> void f(S s = 0, double t = 0);

जो मैंने निम्नलिखित कोड के साथ साबित किया है:

#include <iostream>
#include <string>
#include <sstream>
#include <ctype.h>

template <typename T> T prettify(T t) { return t; }
std::string prettify(char c) { 
    std::stringstream ss;
    if (isprint((unsigned char)c)) {
        ss << "'" << c << "'";
    } else {
        ss << (int)c;
    }
    return ss.str();
}

template <typename S, typename T> void g(S s, T t){
    std::cout << "f<" << typeid(S).name() << "," << typeid(T).name()
        << ">(" << s << "," << prettify(t) << ")\n";
}


template <typename S, typename T> void f(S s = 0, T t = 0){
    g<S,T>(s,t);
}

template <typename S> void f(S s = 0, double t = 0) {
    g<S,double>(s, t);
}

int main() {
        f(1, 'c');         // f<int,char>(1,'c')
        f(1);              // f<int,double>(1,0)
//        f();               // error: T cannot be deduced
        f<int>();          // f<int,double>(0,0)
        f<int,char>();     // f<int,char>(0,0)
}

मुद्रित आउटपुट प्रत्येक कॉल के लिए टिप्पणियों को f से मेल खाता है, और टिप्पणी-आउट कॉल अपेक्षा के अनुरूप संकलन करने में विफल रहता है।

इसलिए मुझे संदेह है कि डिफ़ॉल्ट टेम्पलेट पैरामीटर "आवश्यक नहीं हैं", लेकिन संभवतः केवल उसी अर्थ में कि डिफ़ॉल्ट फ़ंक्शन तर्क "आवश्यक नहीं हैं"। जैसा कि स्ट्रॉस्ट्रुप की दोष रिपोर्ट इंगित करती है, गैर-कटौती किए गए मापदंडों का जोड़ किसी को भी महसूस करने के लिए बहुत देर हो चुकी थी और / या वास्तव में सराहना करती है कि इसने चूक को उपयोगी बना दिया। तो वर्तमान स्थिति फंक्शन टेम्प्लेट के एक संस्करण पर आधारित है जो कभी मानक नहीं था।


@ नमस्कार: तो अंडा चिकन की तुलना में तेजी से चल रहा था? :) दिलचस्प। धन्यवाद।
अरमान

1
शायद उन चीजों में से सिर्फ एक। C ++ मानकीकरण प्रक्रिया भाग में धीमी गति से चलती है ताकि लोगों को यह महसूस करने का समय मिल सके जब कोई बदलाव मानक में कहीं और अवसर या कठिनाइयाँ पैदा करता है। ड्राफ्ट मानक को लागू करने वाले लोगों द्वारा उम्मीद के साथ कठिनाइयों को पकड़ा जाता है, जब वे विरोधाभास या अस्पष्टता को देखते हैं। उन चीज़ों की अनुमति देने के अवसर जिन्हें पहले अनुमति नहीं थी, किसी ऐसे व्यक्ति पर भरोसा करें जो यह नोट करना चाहता है कि यह कोड गैरकानूनी होना चाहिए ...
स्टीव जेसप

2
आपके लिए एक और template<typename T = void> int SomeFunction();:। यहाँ टेम्पलेट पैरामीटर का उपयोग कभी नहीं किया जाता है, और वास्तव में फ़ंक्शन को कभी नहीं बुलाया जाता है; यह निर्दिष्ट करने के लिए एकमात्र स्थान decltypeया में है sizeof। नाम जानबूझकर किसी अन्य फ़ंक्शन के नाम से मेल खाता है, लेकिन यह एक टेम्पलेट है इसका मतलब यह है कि संकलक मुक्त फ़ंक्शन को पसंद करेगा यदि यह मौजूद है। दो का उपयोग SFINAE में डिफ़ॉल्ट व्यवहार प्रदान करने के लिए किया जाता है जहां एक फ़ंक्शन परिभाषा गायब है।
टॉम

4

विंडोज पर, विजुअल स्टूडियो के सभी संस्करणों के साथ आप इस त्रुटि ( C4519 ) को चेतावनी में बदल सकते हैं या इसे इस तरह अक्षम कर सकते हैं:

#ifdef  _MSC_VER
#pragma warning(1 : 4519) // convert error C4519 to warning
// #pragma warning(disable : 4519) // disable error C4519
#endif

अधिक विवरण यहां देखें ।


1
ध्यान दें कि, जबकि यह "डिफ़ॉल्ट टेम्पलेट तर्क को केवल एक क्लास टेम्पलेट पर अनुमत है" संदेश है, यह वास्तव में टेम्पलेट तत्काल प्रक्रिया प्रदान किए गए मूल्य का उपयोग नहीं करता है । इसके लिए VS2013 (या किसी भी अन्य संकलक की आवश्यकता है जो C ++ 11 दोष 226 "फ़ंक्शन टेम्प्लेट के लिए डिफ़ॉल्ट टेम्पलेट तर्क" पूरा कर चुका है)
puetzk

1

मैं जो प्रयोग कर रहा हूं वह अगली चाल है:

कहते हैं कि आप इस तरह से कार्य करना चाहते हैं:

template <typename E, typename ARR_E = MyArray_t<E> > void doStuff(ARR_E array)
{
    E one(1);
    array.add( one );
}

आपको अनुमति नहीं दी जाएगी, लेकिन मैं अगले तरीके से करता हूं:

template <typename T>
struct MyArray_t {
void add(T i) 
{
    // ...
}
};

template <typename E, typename ARR_E = MyArray_t<E> >
class worker {
public:
    /*static - as you wish */ ARR_E* parr_;
    void doStuff(); /* do not make this one static also, MSVC complains */
};

template <typename E, typename ARR_E>
void worker<E, ARR_E>::doStuff()
{
    E one(1);
    parr_->add( one );
}

तो इस तरह से आप इसे इस तरह से उपयोग कर सकते हैं:

MyArray_t<int> my_array;
worker<int> w;
w.parr_ = &arr;
w.doStuff();

जैसा कि हम दूसरे पैरामीटर को स्पष्ट रूप से सेट करने की कोई आवश्यकता नहीं देख सकते हैं। शायद यह किसी के लिए उपयोगी होगा।


यह निश्चित रूप से एक जवाब नहीं है।
पिल्ले

@deadmg - क्या आप बता सकते हैं क्यों? हम सभी C ++ टेम्पलेट गुरु नहीं हैं। धन्यवाद।
केव

यह एक वर्कअराउंड है जो बहुत साफ-सुथरा है लेकिन इसमें उन सभी मामलों को शामिल नहीं किया जाएगा जो आप चाहते हैं। उदाहरण के लिए, आप इसे एक निर्माता पर कैसे लागू करेंगे?
तिबरियू साविन

@TiberiuSavin - अगर मैंने आपको सही तरीके से समझा है, तो आप इस तरह कर सकते हैं: टेम्पलेट <टाइपनेम ई, टाइपरनेम ARR_E> कार्यकर्ता <E, ARR_E> :: कार्यकर्ता (ARR_E * parr) {parr_ = parr; }। और फिर इसे इस तरह से उपयोग करें: कार्यकर्ता <int> w2 (& my_array);
एलारीक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.