खोज…


परिचय

कक्षाएं, फ़ंक्शंस, और (C ++ 14 के बाद से) चर टेम्प्लेट किए जा सकते हैं। एक टेम्पलेट कुछ मुक्त मापदंडों के साथ कोड का एक टुकड़ा है जो सभी मापदंडों को निर्दिष्ट करने पर एक ठोस वर्ग, फ़ंक्शन या चर बन जाएगा। पैरामीटर प्रकार, मान या स्वयं टेम्पलेट हो सकते हैं। एक प्रसिद्ध टेम्पलेट std::vector , जो एक ठोस कंटेनर प्रकार बन जाता है जब तत्व प्रकार निर्दिष्ट किया जाता है, उदाहरण के लिए, std::vector<int>

वाक्य - विन्यास

  • टेम्पलेट < टेम्पलेट-पैरामीटर-सूची > घोषणा
  • निर्यात टेम्पलेट < टेम्पलेट-पैरामीटर-सूची > घोषणा / * C ++ 11 * / तक
  • टेम्पलेट <> घोषणा
  • टेम्पलेट की घोषणा
  • बाहरी टेम्पलेट घोषणा / * C ++ 11 * / के बाद से
  • टेम्पलेट < टेम्पलेट-पैरामीटर-सूची > वर्ग ... ( ऑप्ट ) पहचानकर्ता ( ऑप्ट )
  • टेम्पलेट < टेम्पलेट-पैरामीटर-सूची > वर्ग पहचानकर्ता ( ऑप्ट ) = आईडी-अभिव्यक्ति
  • टेम्पलेट < टेम्पलेट-पैरामीटर-सूची > टाइपनेम ... ( ऑप्ट ) पहचानकर्ता ( ऑप्ट ) / * चूंकि C ++ 17 * /
  • टेम्पलेट < टेम्पलेट-पैरामीटर-सूची > टाइपनेम पहचानकर्ता ( ऑप्ट ) = आईडी-अभिव्यक्ति / * C ++ 17 * / के बाद से
  • उपसर्ग-अभिव्यक्ति । टेम्पलेट आईडी-अभिव्यक्ति
  • उपसर्ग-अभिव्यक्ति -> टेम्पलेट आईडी-अभिव्यक्ति
  • नेस्टेड-नाम-निर्दिष्ट template सरल-टेम्पलेट-आईडी ::

टिप्पणियों

template शब्द C ++ भाषा में संदर्भ के आधार पर पांच अलग-अलग अर्थों वाला एक कीवर्ड है।

  1. जब <> में संलग्न टेम्प्लेट मापदंडों की एक सूची का अनुसरण किया जाता है, तो यह एक क्लास टेम्पलेट , एक फ़ंक्शन टेम्प्लेट या मौजूदा टेम्पलेट के आंशिक विशेषज्ञता जैसे टेम्पलेट की घोषणा करता है।

    template <class T>
    void increment(T& x) { ++x; }
    
  2. जब खाली <> द्वारा पीछा किया जाता है, तो यह एक स्पष्ट (पूर्ण) विशेषज्ञता घोषित करता है।

    template <class T>
    void print(T x);
    
    template <> // <-- keyword used in this sense here
    void print(const char* s) {
        // output the content of the string
        printf("%s\n", s);
    }
    
  3. जब <> बिना एक घोषणा के बाद, यह एक स्पष्ट तात्कालिकता घोषणा या परिभाषा बनाता है।

    template <class T>
    std::set<T> make_singleton(T x) { return std::set<T>(x); }
    
    template std::set<int> make_singleton(int x); // <-- keyword used in this sense here
    
  4. एक टेम्पलेट पैरामीटर सूची के भीतर, यह एक टेम्पलेट टेम्पलेट पैरामीटर का परिचय देता है।

    template <class T, template <class U> class Alloc>
    //                 ^^^^^^^^ keyword used in this sense here
    class List {
        struct Node {
            T value;
            Node* next;
        };
        Alloc<Node> allocator;
        Node* allocate_node() {
            return allocator.allocate(sizeof(T));
        }
        // ...
    };
    
  5. गुंजाइश रिज़ॉल्यूशन ऑपरेटर के बाद :: और वर्ग सदस्य एक्सेस ऑपरेटर . और -> , यह निर्दिष्ट करता है कि निम्नलिखित नाम एक टेम्पलेट है।

    struct Allocator {
        template <class T>
        T* allocate();
    };
    
    template <class T, class Alloc>
    class List {
        struct Node {
            T value;
            Node* next;
        }
        Alloc allocator;
        Node* allocate_node() {
            // return allocator.allocate<Node>();       // error: < and > are interpreted as
                                                        // comparison operators
            return allocator.template allocate<Node>(); // ok; allocate is a template
            //               ^^^^^^^^ keyword used in this sense here
        }
    };
    

C ++ 11 से पहले, एक export कीवर्ड के साथ घोषित किया जा सकता है, जो इसे एक्सपोर्ट किए गए टेम्प्लेट में बनाता है। एक निर्यात किए गए टेम्पलेट की परिभाषा को हर अनुवाद इकाई में मौजूद होने की आवश्यकता नहीं है जिसमें टेम्पलेट को त्वरित किया जाता है। उदाहरण के लिए, निम्नलिखित काम करना चाहिए था:

foo.h :

#ifndef FOO_H
#define FOO_H
export template <class T> T identity(T x);
#endif

foo.cpp :

#include "foo.h"
template <class T> T identity(T x) { return x; }

main.cpp :

#include "foo.h"
int main() {
    const int x = identity(42); // x is 42
}

कार्यान्वयन की कठिनाई के कारण, export कीवर्ड को अधिकांश प्रमुख कंपाइलरों द्वारा समर्थित नहीं किया गया था। इसे C ++ 11 में हटा दिया गया था; अब, export खोजशब्द का उपयोग करना अवैध है। इसके बजाय, आमतौर पर हेडर में टेम्पलेट को परिभाषित करना आवश्यक होता है (गैर-टेम्पलेट फ़ंक्शन के विपरीत, जो आमतौर पर हेडर में परिभाषित नहीं होते हैं)। देखें कि हेडर फ़ाइल में केवल टेम्प्लेट क्यों लागू किए जा सकते हैं?

समारोह टेम्पलेट्स

टेंपलेटिंग को एक ही प्रभाव के साथ कार्यों (साथ ही अधिक पारंपरिक संरचनाओं) पर भी लागू किया जा सकता है।

// 'T' stands for the unknown type
// Both of our arguments will be of the same type.
template<typename T>
void printSum(T add1, T add2)
{
    std::cout << (add1 + add2) << std::endl;
}

इसके बाद इसे स्ट्रक्चर टेम्प्लेट्स की तरह ही इस्तेमाल किया जा सकता है।

printSum<int>(4, 5);
printSum<float>(4.5f, 8.9f);

इन दोनों मामलों में पैरामीटर के प्रकारों को बदलने के लिए टेम्पलेट तर्क का उपयोग किया जाता है; परिणाम सामान्य सी ++ फ़ंक्शन की तरह काम करता है (यदि पैरामीटर टेम्पलेट से मेल नहीं खाते हैं तो कंपाइलर मानक रूपांतरण लागू करता है)।

टेम्प्लेट फ़ंक्शंस की एक अतिरिक्त संपत्ति (टेम्प्लेट क्लासेस के विपरीत) यह है कि कंपाइलर फ़ंक्शन में दिए गए मापदंडों के आधार पर टेम्प्लेट पैरामीटरों का अनुमान लगा सकता है।

printSum(4, 5);     // Both parameters are int.
                    // This allows the compiler deduce that the type
                    // T is also int.

printSum(5.0, 4);   // In this case the parameters are two different types.
                    // The compiler is unable to deduce the type of T
                    // because there are contradictions. As a result
                    // this is a compile time error.

यह सुविधा हमें कोड को सरल बनाने की अनुमति देती है जब हम टेम्पलेट संरचनाओं और कार्यों को जोड़ते हैं। मानक पुस्तकालय में एक सामान्य पैटर्न है जो हमें हेल्पर फ़ंक्शन make_X() का उपयोग करके template structure X बनाने की अनुमति देता है।

// The make_X pattern looks like this.
// 1) A template structure with 1 or more template types.
template<typename T1, typename T2>
struct MyPair
{
    T1      first;
    T2      second;
};
// 2) A make function that has a parameter type for
//    each template parameter in the template structure.
template<typename T1, typename T2>
MyPair<T1, T2> make_MyPair(T1 t1, T2 t2)
{
    return MyPair<T1, T2>{t1, t2};
}

यह कैसे मदद करता है?

auto val1 = MyPair<int, float>{5, 8.7};     // Create object explicitly defining the types
auto val2 = make_MyPair(5, 8.7);            // Create object using the types of the paramters.
                                            // In this code both val1 and val2 are the same
                                            // type.

नोट: यह कोड को छोटा करने के लिए नहीं बनाया गया है। यह कोड को और अधिक मजबूत बनाने के लिए बनाया गया है। यह कई स्थानों के बजाय एक ही स्थान पर कोड को बदलकर प्रकारों को बदलने की अनुमति देता है।

तर्क अग्रेषित करना

टेम्प्लेट अग्रेषण संदर्भ का उपयोग करते हुए अंतराल और अंतराल दोनों संदर्भों को स्वीकार कर सकता है:

template <typename T>
void f(T &&t);

इस मामले में, के वास्तविक प्रकार t संदर्भ के आधार पर निष्कर्ष निकाला किया जाएगा:

struct X { };

X x;
f(x); // calls f<X&>(x)
f(X()); // calls f<X>(x)

पहले मामले में, प्रकार T करने के लिए संदर्भ के रूप में निष्कर्ष निकाला है X ( X& ), और के प्रकार के t के लिए lvalue संदर्भ है X , जबकि दूसरे मामले में के प्रकार के T के रूप में निष्कर्ष निकाला है X और के प्रकार t rvalue संदर्भ के रूप में से X ( X&& )।

ध्यान दें: यह कि पहले मामले में, लायक देख रही है decltype(t) के रूप में ही है T , लेकिन दूसरे में नहीं।

पूरी तरह से आगे करने के लिए t एक और कार्य करने के लिए, चाहे वह एक lvalue या rvalue संदर्भ है, एक का उपयोग करना चाहिए std::forward :

template <typename T>
void f(T &&t) {
    g(std::forward<T>(t));
}

अग्रेषण संदर्भों का उपयोग वैरेडिक टेम्प्लेट के साथ किया जा सकता है:

template <typename... Args>
void f(Args&&... args) {
    g(std::forward<Args>(args)...);
}

नोट: अग्रेषण संदर्भों को केवल टेम्पलेट मापदंडों के लिए उपयोग किया जा सकता है, उदाहरण के लिए, निम्नलिखित कोड में, v एक प्रतिद्वंद्विता संदर्भ है, अग्रेषण संदर्भ नहीं:

#include <vector>

template <typename T>
void f(std::vector<T> &&v);

बेसिक क्लास टेम्पलेट

वर्ग टेम्पलेट का मूल विचार यह है कि टेम्पलेट पैरामीटर को संकलन समय पर एक प्रकार से प्रतिस्थापित किया जाता है। परिणाम यह है कि एक ही वर्ग को कई प्रकारों के लिए पुन: उपयोग किया जा सकता है। उपयोगकर्ता निर्दिष्ट करता है कि किस प्रकार का उपयोग तब किया जाएगा जब कक्षा का एक चर घोषित किया जाता है। इसके तीन उदाहरण main() में दिखाए गए हैं:

#include <iostream>
using std::cout;

template <typename T>         // A simple class to hold one number of any type
class Number {
public:
    void setNum(T n);         // Sets the class field to the given number
    T plus1() const;          // returns class field's "follower"
private:
    T num;                    // Class field
};

template <typename T>         // Set the class field to the given number
void Number<T>::setNum(T n) {
    num = n;
}

template <typename T>         // returns class field's "follower"
T Number<T>::plus1() const {
    return num + 1;
}

int main() {
    Number<int> anInt;        // Test with an integer (int replaces T in the class)
    anInt.setNum(1);
    cout << "My integer + 1 is " << anInt.plus1() << "\n";     // Prints 2

    Number<double> aDouble;   // Test with a double
    aDouble.setNum(3.1415926535897);
    cout << "My double + 1 is " << aDouble.plus1() << "\n";    // Prints 4.14159

    Number<float> aFloat;     // Test with a float
    aFloat.setNum(1.4);
    cout << "My float + 1 is " << aFloat.plus1() << "\n";      // Prints 2.4

    return 0;  // Successful completion
}

खाका विशेषज्ञता

आप टेम्प्लेट क्लास / विधि के विशिष्ट तात्कालिकता के लिए कार्यान्वयन को परिभाषित कर सकते हैं।

उदाहरण के लिए यदि आपके पास:

template <typename T>
T sqrt(T t) { /* Some generic implementation */ }

आप तब लिख सकते हैं:

template<>
int sqrt<int>(int i) { /* Highly optimized integer implementation */ }

तब sqrt(4.0) लिखने वाले उपयोगकर्ता को जेनेरिक कार्यान्वयन मिलेगा जबकि sqrt(4) को विशेष कार्यान्वयन मिलेगा।

आंशिक टेम्पलेट विशेषज्ञता

एक पूर्ण टेम्पलेट विशेषज्ञता के विपरीत आंशिक टेम्पलेट विशेषज्ञता मौजूदा टेम्पलेट के कुछ तर्कों के साथ टेम्पलेट पेश करने की अनुमति देती है। आंशिक टेम्पलेट विशेषज्ञता केवल टेम्पलेट वर्ग / संरचनाओं के लिए उपलब्ध है:

// Common case:
template<typename T, typename U>
struct S {
    T t_val;
    U u_val;
};

// Special case when the first template argument is fixed to int
template<typename V>
struct S<int, V> {
    double another_value;
    int foo(double arg) {// Do something}
};

जैसा कि ऊपर दिखाया गया है, आंशिक टेम्पलेट विशेषज्ञ डेटा और फ़ंक्शन सदस्यों के पूरी तरह से अलग सेट पेश कर सकते हैं।

जब आंशिक रूप से विशिष्ट टेम्पलेट को त्वरित किया जाता है, तो सबसे उपयुक्त विशेषज्ञता का चयन किया जाता है। उदाहरण के लिए, आइए एक टेम्पलेट और दो आंशिक विशेषज्ञता को परिभाषित करें:

template<typename T, typename U, typename V>
struct S {
    static void foo() {
        std::cout << "General case\n";
    }
};

template<typename U, typename V>
struct S<int, U, V> {
    static void foo() {
        std::cout << "T = int\n";
    }
};

template<typename V>
struct S<int, double, V> {
    static void foo() {
        std::cout << "T = int, U = double\n";
    }
};

अब निम्नलिखित कॉल:

S<std::string, int, double>::foo();
S<int, float, std::string>::foo();
S<int, double, std::string>::foo();

छप जाएगा

General case
T = int
T = int, U = double

समारोह टेम्पलेट केवल पूरी तरह से विशेष हो सकते हैं:

template<typename T, typename U>
void foo(T t, U u) {
    std::cout << "General case: " << t << " " << u << std::endl;
}

// OK.
template<>
void foo<int, int>(int a1, int a2) {
    std::cout << "Two ints: " << a1 << " " << a2 << std::endl;
}

void invoke_foo() {
    foo(1, 2.1); // Prints "General case: 1 2.1"
    foo(1,2);    // Prints "Two ints: 1 2"
}

// Compilation error: partial function specialization is not allowed.
template<typename U>
void foo<std::string, U>(std::string t, U u) {
    std::cout << "General case: " << t << " " << u << std::endl;
}

डिफ़ॉल्ट टेम्पलेट पैरामीटर मान

जैसे फ़ंक्शन तर्कों के मामले में, टेम्पलेट पैरामीटर उनके डिफ़ॉल्ट मान हो सकते हैं। डिफ़ॉल्ट मान वाले सभी टेम्पलेट पैरामीटर को टेम्पलेट पैरामीटर सूची के अंत में घोषित किया जाना है। मूल विचार यह है कि डिफ़ॉल्ट मान के साथ टेम्पलेट पैरामीटर को छोड़ दिया जा सकता है जबकि टेम्पलेट तात्कालिकता।

डिफ़ॉल्ट टेम्पलेट पैरामीटर मान उपयोग का सरल उदाहरण:

template <class T, size_t N = 10>
struct my_array {
    T arr[N];
};

int main() {
    /* Default parameter is ignored, N = 5 */
    my_array<int, 5> a;

    /* Print the length of a.arr: 5 */
    std::cout << sizeof(a.arr) / sizeof(int) << std::endl;

    /* Last parameter is omitted, N = 10 */
    my_array<int> b;

    /* Print the length of a.arr: 10 */
    std::cout << sizeof(b.arr) / sizeof(int) << std::endl;
}

उपनाम का खाका

सी ++ 11

मूल उदाहरण:

template<typename T> using pointer = T*;

यह परिभाषा pointer<T> T* का उपनाम है। उदाहरण के लिए:

pointer<int> p = new int; // equivalent to: int* p = new int;

अन्य टेम्पलेट विशेष नहीं किए जा सकते। हालाँकि, उस कार्यक्षमता को अप्रत्यक्ष रूप से एक संरचना में एक नेस्टेड प्रकार से संदर्भित करके प्राप्त किया जा सकता है:

template<typename T>
 struct nonconst_pointer_helper { typedef T* type; };

template<typename T>
 struct nonconst_pointer_helper<T const> { typedef T* type; };

template<typename T> using nonconst_pointer = nonconst_pointer_helper<T>::type;

टेम्प्लेट टेम्पलेट पैरामीटर

कभी-कभी हम इसके मानों को ठीक किए बिना एक टेम्पलेट प्रकार में उत्तीर्ण करना चाहते हैं। यह वही है जो टेम्पलेट टेम्पलेट पैरामीटर के लिए बनाया गया है। बहुत ही सरल टेम्पलेट टेम्पलेट पैरामीटर उदाहरण:

template <class T>
struct Tag1 { };

template <class T>
struct Tag2 { };

template <template <class> class Tag>
struct IntTag {
   typedef Tag<int> type;
};

int main() {
   IntTag<Tag1>::type t;
}
सी ++ 11
#include <vector>
#include <iostream>

template <class T, template <class...> class C, class U>
C<T> cast_all(const C<U> &c) {
   C<T> result(c.begin(), c.end());
   return result;
}

int main() {
   std::vector<float> vf = {1.2, 2.6, 3.7};
   auto vi = cast_all<int>(vf);
   for(auto &&i: vi) {
      std::cout << i << std::endl;
   }
}

ऑटो के साथ गैर-टाइप टेम्पलेट तर्क की घोषणा

C ++ 17 से पहले, जब कोई टेम्प्लेट नॉन-टाइप पैरामीटर लिखता है, तो आपको पहले उसका टाइप बताना होगा। तो एक सामान्य पैटर्न कुछ इस तरह लिख गया:

template <class T, T N>
struct integral_constant {
    using type = T;
    static constexpr T value = N;
};

using five = integral_constant<int, 5>;

लेकिन जटिल अभिव्यक्तियों के लिए, कुछ इस तरह का उपयोग करते हुए decltype(expr), expr लिखना decltype(expr), expr जब decltype(expr), expr टेम्प्लेट करते हैं। समाधान इस मुहावरे को सरल बनाना है और बस auto अनुमति देना है:

सी ++ 17
template <auto N>
struct integral_constant {
    using type = decltype(N); 
    static constexpr type value = N;
};

using five = integral_constant<5>;

Unique_ptr के लिए कस्टम डिलेटर खाली करें

एक अच्छा प्रेरक उदाहरण unique_ptr लिए कस्टम unique_ptr साथ खाली आधार अनुकूलन को संयोजित करने के प्रयास से आ सकता है। विभिन्न सी एपीआई हटाने वालों के पास अलग-अलग रिटर्न प्रकार होते हैं, लेकिन हम परवाह नहीं करते हैं - हम बस किसी भी कार्य के लिए कुछ करना चाहते हैं:

template <auto DeleteFn>
struct FunctionDeleter {
    template <class T>
    void operator()(T* ptr) const {
        DeleteFn(ptr);
    }
};

template <T, auto DeleteFn>
using unique_ptr_deleter = std::unique_ptr<T, FunctionDeleter<DeleteFn>>;

और अब आप किसी भी फ़ंक्शन पॉइंटर का उपयोग कर सकते हैं जो कि टाइप नॉन-टाइप पैरामीटर के रूप में टाइप T का एक तर्क ले सकता है, चाहे रिटर्न प्रकार की परवाह किए बिना, और इसमें से एक नो-साइज़ ओवरहेड unique_ptr :

unique_ptr_deleter<std::FILE, std::fclose> p;

गैर-प्रकार टेम्पलेट पैरामीटर

एक टेम्पलेट पैरामीटर के रूप में प्रकारों के अलावा हमें निम्न मानदंडों में से एक को पूरा करने वाले निरंतर अभिव्यक्तियों के मूल्यों की घोषणा करने की अनुमति है:

  • अभिन्न या गणना प्रकार,
  • सूचक वस्तु या सूचक कार्य करने के लिए,
  • ऑब्जेक्ट के लिए लैवल्यू संदर्भ या फ़ंक्शन के लिए लैवल्यू संदर्भ,
  • सदस्य को सूचक,
  • std::nullptr_t

सभी टेम्प्लेट मापदंडों की तरह, गैर-प्रकार के टेम्प्लेट पैरामीटर को स्पष्ट रूप से निर्दिष्ट किया जा सकता है, डिफ़ॉल्ट रूप से या टेम्पलेट तर्क कटौती के माध्यम से प्राप्त किया जा सकता है।

गैर-प्रकार टेम्पलेट पैरामीटर उपयोग का उदाहरण:

#include <iostream>

template<typename T, std::size_t size>
std::size_t size_of(T (&anArray)[size])  // Pass array by reference. Requires.
{                                        // an exact size. We allow all sizes
    return size;                         // by using a template "size".
}

int main()
{
    char anArrayOfChar[15];
    std::cout << "anArrayOfChar: " << size_of(anArrayOfChar) << "\n";

    int  anArrayOfData[] = {1,2,3,4,5,6,7,8,9};
    std::cout << "anArrayOfData: " << size_of(anArrayOfData) << "\n";
}

दोनों प्रकार और गैर-प्रकार के टेम्पलेट मापदंडों को स्पष्ट रूप से निर्दिष्ट करने का उदाहरण:

#include <array>
int main ()
{
    std::array<int, 5> foo; // int is a type parameter, 5 is non-type
}

गैर-प्रकार के टेम्पलेट पैरामीटर टेम्पलेट पुनरावृत्ति को प्राप्त करने के तरीकों में से एक हैं और मेटाप्रोग्रामिंग करने में सक्षम हैं।

वैरिएडिक टेम्प्लेट डेटा संरचनाएं

सी ++ 14

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

template<typename ... T>
struct DataStructure {};

यह पहले से ही हमें एक खाली संरचना को परिभाषित करने की अनुमति देता है, DataStructure<> data , जो कि अभी तक बहुत उपयोगी नहीं है।

अगला पुनरावर्ती मामला विशेषज्ञता आता है:

template<typename T, typename ... Rest>
struct DataStructure<T, Rest ...>
{
    DataStructure(const T& first, const Rest& ... rest)
        : first(first)
        , rest(rest...)
    {}
    
    T first;                                
    DataStructure<Rest ... > rest;
};

यह अब हमारे लिए पर्याप्त डेटा संरचनाएं बनाने के लिए पर्याप्त है, जैसे DataStructure<int, float, std::string> data(1, 2.1, "hello")

तो क्या चल रहा है? सबसे पहले, ध्यान दें कि यह एक विशेषज्ञता जिसका आवश्यकता है कि कम से कम एक variadic टेम्पलेट पैरामीटर (अर्थात् है T ऊपर) मौजूद है, जबकि पैक के विशिष्ट श्रृंगार के बारे में ध्यान रखने वाली नहीं Rest । यह जानते हुए कि T मौजूद है, first अपने डेटा सदस्य की परिभाषा की अनुमति देता है। बाकी डेटा को DataStructure<Rest ... > rest रूप में पुन: पैक किया जाता है। कंस्ट्रक्टर उन दोनों सदस्यों को आरंभ करता है, जिसमें एक पुनरावर्ती कंस्ट्रक्टर rest सदस्य को कॉल करता है।

इसे बेहतर समझने के लिए, हम एक उदाहरण के माध्यम से काम कर सकते हैं: मान लीजिए कि आपके पास एक घोषणा है DataStructure<int, float> data । घोषणा पहले विशेषज्ञता के खिलाफ मेल खाती है, int first और DataStructure<float> rest डेटा सदस्यों के साथ एक संरचना की उपज। rest परिभाषा फिर से इस विशेषज्ञता से मेल खाती है, जो float first अपना float first और DataStructure<> rest सदस्य। अंत में यह पिछले rest , आधार दर-मामला defintion के खिलाफ मेल खाता है एक खाली संरचना का निर्माण किया।

आप इसकी कल्पना इस प्रकार कर सकते हैं:

DataStructure<int, float>
   -> int first
   -> DataStructure<float> rest
         -> float first
         -> DataStructure<> rest
              -> (empty)

अब हमारे पास डेटा संरचना है, लेकिन अभी तक यह बहुत उपयोगी नहीं है क्योंकि हम व्यक्तिगत डेटा तत्वों तक आसानी से नहीं पहुँच सकते हैं (उदाहरण के लिए DataStructure<int, float, std::string> data के अंतिम सदस्य तक पहुँचने के लिए DataStructure<int, float, std::string> data हमें DataStructure<int, float, std::string> data का उपयोग करना होगा data.rest.rest.first , जो बिल्कुल उपयोगकर्ता के अनुकूल नहीं है)। हम एक जोड़ने तो get यह करने के लिए विधि (केवल आधार दर-मामला संरचना के रूप में विशेषज्ञता की जरूरत के लिए कोई डाटा नहीं है get ):

template<typename T, typename ... Rest>
struct DataStructure<T, Rest ...>
{
    ...
    template<size_t idx>
    auto get()
    {
        return GetHelper<idx, DataStructure<T,Rest...>>::get(*this);
    }
    ...
};

आप देख सकते हैं इस get (वैसे-वैसे उपयोग जैसी चीजों हो सकता सदस्य है कि जरूरत है के सूचकांक पर इस बार - सदस्य समारोह में ही टेम्प्लेट की गई है data.get<1>() के समान है, std::tuple )। वास्तविक कार्य एक हेल्पर वर्ग, GetHelper हेल्पर में स्थिर कार्य द्वारा किया जाता है। कारण है कि हम सीधे आवश्यक कार्यक्षमता को परिभाषित नहीं कर सकते DataStructure की get है क्योंकि (जैसा कि हम शीघ्र ही देखेंगे) हम पर विशेषज्ञ की आवश्यकता होगी है idx - लेकिन यह युक्त वर्ग विशेषज्ञता के बिना एक टेम्पलेट सदस्य समारोह विशेषज्ञ करना संभव नहीं है टेम्पलेट। ध्यान दें, यहां C ++ 14-शैली के auto का उपयोग हमारे जीवन को काफी सरल बनाता है अन्यथा हमें वापसी प्रकार के लिए काफी जटिल अभिव्यक्ति की आवश्यकता होगी।

तो सहायक वर्ग के लिए। इस बार हमें एक खाली फॉरवर्ड घोषणा और दो विशेषज्ञता की आवश्यकता होगी। पहली घोषणा:

template<size_t idx, typename T>
struct GetHelper;

अब आधार-मामला (जब idx==0 )। इस मामले में हम first सदस्य को वापस करते हैं:

template<typename T, typename ... Rest>
struct GetHelper<0, DataStructure<T, Rest ... >>
{
    static T get(DataStructure<T, Rest...>& data)
    {
        return data.first;
    }
};

पुनरावर्ती मामले में, हम idx को GetHelper हैं और rest सदस्य के लिए GetHelper आह्वान करते हैं:

template<size_t idx, typename T, typename ... Rest>
struct GetHelper<idx, DataStructure<T, Rest ... >>
{
    static auto get(DataStructure<T, Rest...>& data)
    {
        return GetHelper<idx-1, DataStructure<Rest ...>>::get(data.rest);
    }
};

एक उदाहरण के माध्यम से काम करने के लिए, मान लें कि हमारे पास DataStructure<int, float> data और हमें data.get<1>() । यह GetHelper<1, DataStructure<int, float>>::get(data) (2nd स्पेशलाइजेशन) को आमंत्रित करता है, जो बदले में GetHelper<0, DataStructure<float>>::get(data.rest) , जो अंत में वापस आ जाता है। (1 विशेषज्ञता द्वारा के रूप में अब idx 0) data.rest.first

तो यह बात है! यहाँ main कार्यप्रणाली में कुछ उदाहरण उपयोग के साथ पूरा कामकाज कोड है:

#include <iostream>

template<size_t idx, typename T>
struct GetHelper;

template<typename ... T>
struct DataStructure
{
};

template<typename T, typename ... Rest>
struct DataStructure<T, Rest ...>
{
    DataStructure(const T& first, const Rest& ... rest)
        : first(first)
        , rest(rest...)
    {}
    
    T first;
    DataStructure<Rest ... > rest;
    
    template<size_t idx>
    auto get()
    {
        return GetHelper<idx, DataStructure<T,Rest...>>::get(*this);
    }
};

template<typename T, typename ... Rest>
struct GetHelper<0, DataStructure<T, Rest ... >>
{
    static T get(DataStructure<T, Rest...>& data)
    {
        return data.first;
    }
};

template<size_t idx, typename T, typename ... Rest>
struct GetHelper<idx, DataStructure<T, Rest ... >>
{
    static auto get(DataStructure<T, Rest...>& data)
    {
        return GetHelper<idx-1, DataStructure<Rest ...>>::get(data.rest);
    }
};

int main()
{
    DataStructure<int, float, std::string> data(1, 2.1, "Hello");
        
    std::cout << data.get<0>() << std::endl;
    std::cout << data.get<1>() << std::endl;
    std::cout << data.get<2>() << std::endl;
    
    return 0;
}

स्पष्ट तात्कालिकता

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

// print_string.h
template <class T>
void print_string(const T* str);

// print_string.cpp
#include "print_string.h"
template void print_string(const char*);
template void print_string(const wchar_t*);

क्योंकि print_string<char> और print_string<wchar_t> को print_string.cpp में स्पष्ट रूप से तात्कालिक रूप से print_string.cpp , लेकिन print_string.cpp करने वाला उन्हें खोजने में सक्षम होगा, भले ही print_string टेम्पलेट हेडर में परिभाषित नहीं किया गया हो। यदि ये स्पष्ट तात्कालिक घोषणाएं मौजूद नहीं थीं, तो एक लिंकर त्रुटि की संभावना होगी। देखें कि हेडर फ़ाइल में केवल टेम्प्लेट क्यों लागू किए जा सकते हैं?

सी ++ 11

यदि extern कीवर्ड द्वारा एक स्पष्ट तात्कालिकता की परिभाषा दी गई है, तो यह इसके बजाय एक स्पष्ट तात्कालिक घोषणा बन जाता है। किसी दिए गए विशेषज्ञता के लिए एक स्पष्ट तात्कालिक घोषणा की उपस्थिति वर्तमान अनुवाद इकाई के भीतर दिए गए विशेषज्ञता के निहित तात्कालिकता को रोकती है। इसके बजाय, उस विशेषज्ञता का एक संदर्भ जो अन्यथा एक अंतर्निहित तात्कालिकता का कारण होगा उसी या किसी अन्य TU में एक स्पष्ट तात्कालिकता परिभाषा को संदर्भित कर सकता है।

foo.h

#ifndef FOO_H
#define FOO_H
template <class T> void foo(T x) {
    // complicated implementation
}
#endif

foo.cpp

#include "foo.h"
// explicit instantiation definitions for common cases
template void foo(int);
template void foo(double);

main.cpp

#include "foo.h"
// we already know foo.cpp has explicit instantiation definitions for these
extern template void foo(double);
int main() {
    foo(42);   // instantiates foo<int> here;
               // wasteful since foo.cpp provides an explicit instantiation already!
    foo(3.14); // does not instantiate foo<double> here;
               // uses instantiation of foo<double> in foo.cpp instead
}


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow