टेम्प्लेटेड क्लास से एकल विधि का खाका विशेषज्ञता


92

हमेशा यह ध्यान में रखते हुए कि निम्न शीर्षक, जिसमें मेरा टेम्पल क्लास है, कम से कम दो .CPPफाइलों में शामिल है , यह कोड सही तरीके से संकलित है:

template <class T>
class TClass 
{
public:
  void doSomething(std::vector<T> * v);
};

template <class T>
void TClass<T>::doSomething(std::vector<T> * v) {
  // Do something with a vector of a generic T
}

template <>
inline void TClass<int>::doSomething(std::vector<int> * v) {
  // Do something with a vector of int's
}

लेकिन विशेषज्ञता विधि में इनलाइन पर ध्यान दें। विधि को अधिक बार एक बार परिभाषित किए जाने के कारण लिंकर त्रुटि (VS2008 में LNK2005) से बचना आवश्यक है। मैं इसे समझता हूं क्योंकि AFAIK एक पूर्ण टेम्पलेट विशेषज्ञता एक सरल विधि परिभाषा के समान है।

तो, मैं इसे कैसे निकालूं inline? इसके हर उपयोग में कोड को डुप्लिकेट नहीं किया जाना चाहिए। मैंने Google को खोजा है, एसओ के यहां कुछ प्रश्न पढ़े हैं और सुझाए गए कई समाधानों की कोशिश की है लेकिन कोई भी सफलतापूर्वक नहीं बनाया गया है (कम से कम वीएस 2008 में नहीं)।

धन्यवाद!


4
आप इनलाइन को क्यों निकालना चाहते हैं? क्या आप इसे सौंदर्य से अप्रसन्न पाते हैं? क्या आपको लगता है कि यह आपके कोड का अर्थ बदल देता है?
मार्टिन यॉर्क

1
क्योंकि अगर यह तरीका "लंबा" होगा और कई जगहों पर इस्तेमाल किया जाएगा तो मुझे इसका बाइनरी कोड हर जगह कॉपी किया जाएगा, है ना? मैंने इस प्रश्न में समझाने की कोशिश की, लेकिन मुझे लगता है कि यह स्पष्ट नहीं था ... :)
चुइंम

@Martin: क्या होगा यदि कार्यान्वयन को बहुत से अन्य कोड की आवश्यकता होती है जो कि cpp फ़ाइल के बजाय इस हेडर द्वारा शामिल किया जाना है?
sbi

जवाबों:


72

साधारण कार्यों के साथ आप घोषणा और कार्यान्वयन का उपयोग कर सकते हैं। अपने हेडर घोषणा में रखें:

template <>
void TClass<int>::doSomething(std::vector<int> * v);

और अपनी cpp-files में से एक में कार्यान्वयन करें:

template <>
void TClass<int>::doSomething(std::vector<int> * v) {
 // Do somtehing with a vector of int's
}

इनलाइन हटाने के लिए मत भूलना (मैं भूल गया और सोचा कि यह समाधान काम नहीं करेगा :))। वीसी ++ 2005 पर जाँच की गई


मैंने पहले भी कम से कम कुछ इसी तरह की कोशिश की थी, लेकिन मुझे अन्य त्रुटियां मिलीं, लेकिन अब जब आपने उल्लेख किया है कि मैं inlineकॉपी / पेस्टिंग को हटाने के लिए भूल गया हूं । इस तरह से यह काम किया!
चुइम

वही टेम्पलेट मुक्त कार्यों पर लागू होता है (वर्ग विधियों के विपरीत)। मुझे अपने फंक्शन स्पेशलाइजेशन के लिए समान लिंकर त्रुटि मिल रही थी। मैंने एक .cpp फ़ाइल में फंक्शन स्पेशलाइज़ेशन के बॉडी को स्थानांतरित किया और हेडर में स्पेशलाइज़ेशन की घोषणा को छोड़ दिया और सब कुछ काम कर गया। धन्यवाद!
एल्डो

मैं बस इस समस्या में भाग गया, और ऊपर ने मेरे लिए इसे हल किया। इसके अतिरिक्त, आपको इस बात का ध्यान रखने की आवश्यकता है कि कंपाइलर टेम्पलेट कोड का विस्तार कहां करता है । यदि यह दो बार किया जाता है, तो कंपाइलर कई परिभाषाओं के बारे में शिकायत करता है।
डिडेरिक

4

आपको CPP फ़ाइल में विशेषज्ञता परिभाषा को स्थानांतरित करने की आवश्यकता है। टेम्प्लेट क्लास के सदस्य फ़ंक्शन के विशेषज्ञता को अनुमति दी जाती है भले ही फ़ंक्शन को टेम्प्लेट घोषित नहीं किया गया हो।


3

कीवर्ड इनलाइन को हटाने का कोई कारण नहीं है।
यह वैसे भी कोड का अर्थ नहीं बदलता है।


प्रश्न टिप्पणी से प्रतिलिपि बनाई गई: क्योंकि यदि यह विधि "लंबी" होगी और कई स्थानों पर उपयोग की जाएगी तो मुझे इसका बाइनरी कोड हर जगह कॉपी किया जाएगा, है ना? मैंने इस प्रश्न को समझाने की कोशिश की, लेकिन मुझे लगता है कि यह स्पष्ट नहीं था ... :)
चुइंम

1
नहीं। लिंकर किसी भी अतिरिक्त प्रतियां निकालता है। तो एक आवेदन या काम के भीतर आप केवल एक बार विधि का उदाहरण होगा।
मार्टिन यॉर्क

3
यदि inlineफ़ंक्शन वास्तव में इनबिल्ट हो रहा है (मानक कहता है कि कंपाइलर को संकेत के रूप में लेना चाहिए), तो उन अतिरिक्त प्रतियों को हटाया नहीं जा सकता है। हालाँकि, यह केवल एक संकेत इनलाइन है (इसका प्राथमिक प्रभाव यह कहना है कि "किसी विशेष तरीके से लिंक टकरावों पर त्रुटियों को उत्पन्न न करें")
यक्क - एडम नेवरुमोंट

2

यदि आप जो भी कारण के लिए इनलाइन को हटाना चाहते हैं वह मैक्सिम 1000 का समाधान पूरी तरह से वैध है।

आपकी टिप्पणी में, हालांकि, ऐसा लगता है कि आप मानते हैं कि इनलाइन कीवर्ड का अर्थ है कि उसकी सभी सामग्रियों के साथ फ़ंक्शन हमेशा इनबिल्ड हो जाता है लेकिन AFAIK जो वास्तव में आपके कंपाइलर ऑप्टिमाइज़ेशन पर बहुत अधिक निर्भर करता है।

C ++ FAQ से उद्धरण

नामित करने के कई तरीके हैं कि एक फ़ंक्शन इनलाइन है, जिनमें से कुछ इनलाइन कीवर्ड को शामिल करते हैं, अन्य नहीं करते हैं। कोई फर्क नहीं पड़ता कि आप एक फ़ंक्शन को इनलाइन के रूप में कैसे नामित करते हैं, यह एक अनुरोध है कि कंपाइलर को अनदेखा करने की अनुमति दी जाती है: कंपाइलर इनलाइन का विस्तार कर सकता है कुछ, सभी, या उन स्थानों में से कोई भी जहां आप फ़ंक्शन को इनलाइन के रूप में नामित करते हैं। (निराश मत हो अगर ऐसा लगता है कि निराशाजनक रूप से अस्पष्ट है। ऊपर का लचीलापन वास्तव में एक बहुत बड़ा लाभ है: यह संकलक को बड़े कार्यों को छोटे लोगों से अलग तरीके से व्यवहार करने देता है, साथ ही यह संकलक कोड को उत्पन्न करने देता है जो यदि आप चुनते हैं तो डिबग करना आसान है। सही संकलक विकल्प।)

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


1

मैं यह जोड़ना चाहता हूं कि inlineअगर आप हेडर फ़ाइल में विशेषज्ञता भी छोड़ना चाहते हैं, तो वहां कीवर्ड रखने का एक अच्छा कारण है ।

"सहज रूप से, जब आप पूरी तरह से कुछ करते हैं, तो यह किसी भी तरह से एक टेम्पलेट पैरामीटर पर निर्भर नहीं करता है - इसलिए जब तक आप विशेषज्ञता इनलाइन नहीं बनाते हैं, आपको इसे .h के बजाय एक .cpp फ़ाइल में डालने की आवश्यकता होती है या आप उल्लंघन करते हैं। एक परिभाषा नियम ... "

संदर्भ: https://stackoverflow.com/a/4445772/1294184


0

यह थोड़ा OT है, लेकिन मुझे लगा कि मैं इसे यहाँ छोड़ दूँगा अगर यह किसी और की मदद करे। मैं टेम्प्लेट स्पेशलाइज़ेशन के बारे में गुगली कर रहा था, जिसने मुझे यहाँ तक पहुँचाया, और जबकि @ maxim1000 का उत्तर सही है और अंततः मुझे अपनी समस्याओं का पता लगाने में मदद मिली, मुझे नहीं लगा कि यह बहुतायत से स्पष्ट था।

ओपी की तुलना में मेरी स्थिति थोड़ी अलग है (लेकिन इस जवाब को मैं जितना सोचता हूं, उसे छोड़ देता हूं)। मूल रूप से, मैं सभी प्रकार के वर्गों के साथ एक तीसरे पक्ष के पुस्तकालय का उपयोग कर रहा हूं जो "स्थिति प्रकार" को परिभाषित करता है। इन प्रकारों का दिल बस enumएस है, लेकिन सभी वर्ग एक सामान्य (अमूर्त) माता-पिता से विरासत में मिलते हैं और विभिन्न उपयोगिता कार्य प्रदान करते हैं, जैसे कि ऑपरेटर ओवरलोडिंग और एक static toString(enum type)फ़ंक्शन। प्रत्येक स्थिति enumएक दूसरे से अलग और असंबंधित है। उदाहरण के लिए, एक के enumपास खेत हैं NORMAL, DEGRADED, INOPERABLE, दूसरे के पास कक्षाएं हैं, लेकिन जब से वे अमूर्त हैं मैं उन्हें सीधे नहीं कर सकता। मैं प्रत्येक वर्ग को विस्तारित कर सकता था जिसे मैं उपयोग करना चाहता था, लेकिन अंततः मैंने एक वर्ग बनाने का फैसला किया , जहां कुछ भी ठोस स्थिति होगीAVAILBLE, PENDING, MISSING आदि हैं। मेरा सॉफ्टवेयर विभिन्न घटकों के लिए विभिन्न प्रकार की स्थितियों के प्रबंधन का प्रभारी है। इसके बारे में यह आया कि मैं toStringइन कार्यों के लिए उपयोग करना चाहता थाenumtemplatetypenameenumमैंने परवाह की। संभवतः उस निर्णय के बारे में कुछ बहस हो सकती है, लेकिन मुझे ऐसा लगा कि प्रत्येक अमूर्त enumवर्ग को अपने स्वयं के एक कस्टम के साथ बढ़ाने और अमूर्त कार्यों को लागू करने की तुलना में बहुत कम काम था । और निश्चित रूप से मेरे कोड में, मैं बस कॉल करने में सक्षम होना चाहता था .toString(enum type)और क्या यह उस के स्ट्रिंग प्रतिनिधित्व को प्रिंटenum । चूंकि सभी enumएस पूरी तरह से असंबंधित थे, इसलिए उनमें से प्रत्येक का अपना थाtoStringफ़ंक्शंस जो (कुछ शोध के बाद मैंने सीखा) को टेम्प्लेट विशेषज्ञता का उपयोग करके बुलाया जाना था। यही मुझे यहां ले गया। नीचे एक MCVE है कि इस काम को सही ढंग से करने के लिए मुझे क्या करना था। और वास्तव में मेरा समाधान @ maxim1000 की तुलना में थोड़ा अलग था।

यह s के लिए एक (बहुत सरलीकृत) हेडर फ़ाइल है enum। वास्तव में, प्रत्येक enumवर्ग को अपनी फ़ाइल में परिभाषित किया गया था। यह फ़ाइल उन हेडर फ़ाइलों का प्रतिनिधित्व करती है जो मुझे उस पुस्तकालय के भाग के रूप में प्रदान की जाती हैं जिसका मैं उपयोग कर रहा हूँ:

// file enums.h
#include <string>

class Enum1
{
public:
  enum EnumerationItem
  {
    BEARS1,
    BEARS2,
    BEARS3
  };

  static std::string toString(EnumerationItem e)
  {
    // code for converting e to its string representation,
    // omitted for brevity
  }
};

class Enum2
{
public:
  enum EnumerationItem
  {
    TIGERS1,
    TIGERS2,
    TIGERS3
  };

  static std::string toString(EnumerationItem e)
  {
    // code for converting e to its string representation,
    // omitted for brevity
  }
};

अगली पंक्ति को एक अलग कोड ब्लॉक में अलग करने के लिए इस लाइन को जोड़ना:

// file TemplateExample.h
#include <string>

template <typename T>
class TemplateExample
{
public:
  TemplateExample(T t);
  virtual ~TemplateExample();

  // this is the function I was most concerned about. Unlike @maxim1000's
  // answer where (s)he declared it outside the class with full template
  // parameters, I was able to keep mine declared in the class just like
  // this
  std::string toString();

private:
  T type_;
};

template <typename T>
TemplateExample<T>::TemplateExample(T t)
  : type_(t)
{

}

template <typename T>
TemplateExample<T>::~TemplateExample()
{

}

अगली फ़ाइल

// file TemplateExample.cpp
#include <string>

#include "enums.h"
#include "TemplateExample.h"

// for each enum type, I specify a different toString method, and the
// correct one gets called when I call it on that type.
template <>
std::string TemplateExample<Enum1::EnumerationItem>::toString()
{
  return Enum1::toString(type_);
}

template <>
std::string TemplateExample<Enum2::EnumerationItem>::toString()
{
  return Enum2::toString(type_);
}

अगली फ़ाइल

// and finally, main.cpp
#include <iostream>
#include "TemplateExample.h"
#include "enums.h"

int main()
{
  TemplateExample<Enum1::EnumerationItem> t1(Enum1::EnumerationItem::BEARS1);
  TemplateExample<Enum2::EnumerationItem> t2(Enum2::EnumerationItem::TIGERS3);

  std::cout << t1.toString() << std::endl;
  std::cout << t2.toString() << std::endl;

  return 0;
}

और यह आउटपुट:

BEARS1
TIGERS3

कोई सुराग नहीं अगर यह मेरी समस्या को हल करने के लिए आदर्श समाधान है, लेकिन यह मेरे लिए काम करता है। अब, कोई फर्क नहीं पड़ता कि मैं कितने एन्यूमरेशन प्रकारों का उपयोग कर रहा हूं, मुझे केवल इतना करना है toStringकि .cpp फ़ाइल में विधि के लिए कुछ पंक्तियाँ जोड़नी हैं , और मैं पुस्तकालयों को पहले से परिभाषित toStringविधि का उपयोग कर सकता हूं बिना इसे लागू किए और प्रत्येक का विस्तार किए बिना। enumकक्षा मैं उपयोग करना चाहते हैं।

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