बाहरी टेम्पलेट का उपयोग करना (C ++ 11)


116

चित्र 1: फ़ंक्शन टेम्प्लेट

TemplHeader.h

template<typename T>
void f();

TemplCpp.cpp

template<typename T>
void f(){
   //...
}    
//explicit instantation
template void f<T>();

main.cpp

#include "TemplHeader.h"
extern template void f<T>(); //is this correct?
int main() {
    f<char>();
    return 0;
}

क्या यह उपयोग करने का सही तरीका है extern template, या क्या मैं इस कीवर्ड का उपयोग केवल कक्षा के टेम्पलेट्स के लिए करता हूं जैसा कि चित्र 2 में है?

चित्र 2: वर्ग टेम्पलेट

TemplHeader.h

template<typename T>
class foo {
    T f();
};

TemplCpp.cpp

template<typename T>
void foo<T>::f() {
    //...
}
//explicit instantation
template class foo<int>;

main.cpp

#include "TemplHeader.h"
extern template class foo<int>();
int main() {
    foo<int> test;
    return 0;
}

मुझे पता है कि यह सब एक हेडर फाइल में डालना अच्छा है, लेकिन अगर हम एक से अधिक फाइलों में एक ही पैरामीटर के साथ टेम्पलेट्स को त्वरित करते हैं, तो हमें कई समान परिभाषाएं मिलीं और कंपाइलर त्रुटियों से बचने के लिए उन सभी (एक को छोड़कर) को हटा देगा। मैं कैसे उपयोग extern templateकरूं? क्या हम इसे केवल कक्षाओं के लिए उपयोग कर सकते हैं, या हम इसे कार्यों के लिए भी उपयोग कर सकते हैं?

इसके अलावा, चित्र 1 और चित्र 2 को एक समाधान में विस्तारित किया जा सकता है जहां टेम्पलेट एक ही हेडर फ़ाइल में होते हैं। उस स्थिति में, हमें extern templateकई समान इंस्टेंसेस से बचने के लिए कीवर्ड का उपयोग करना होगा । क्या यह केवल कक्षाओं या कार्यों के लिए ही है?


3
यह एक्सट्रीम टेम्प्लेट्स का सही उपयोग नहीं है ... यह भी संकलन नहीं है
दानी

क्या आप (एक) प्रश्न को स्पष्ट रूप से उद्धृत करने के लिए कुछ समय ले सकते हैं? आप किसके लिए कोड पोस्ट कर रहे हैं? मुझे इससे संबंधित कोई प्रश्न दिखाई नहीं देता है। इसके अलावा, extern template class foo<int>();एक गलती की तरह लगता है।
रात 11:03

@ डैनी> यह चेतावनी संदेश को छोड़कर मेरे दृश्य स्टूडियो 2010 पर ठीक-ठीक संकलन करता है: चेतावनी 1 चेतावनी C4231: गैरमानक एक्सटेंशन का उपयोग किया गया: टेम्पलेट स्पष्ट तात्कालिकता से पहले '
बाहरी

2
@ यह प्रश्न बहुत सरल है: एक्सट्रीम टेम्पलेट कीवर्ड का उपयोग कैसे और कब करें? (बाहरी टेम्पलेट C ++ 0x नया भविष्य btw है) आपने कहा "इसके अलावा, बाहरी टेम्पलेट वर्ग foo <int> (), एक गलती की तरह लगता है।" नहीं, यह नहीं है, मेरे पास नई C ++ पुस्तक है और यह मेरी पुस्तक से उदाहरण है।
कोडकिडी

1
@codekiddy: तब दृश्य स्टूडियो वास्तव में बेवकूफ होता है .. दूसरे में प्रोटोटाइप कार्यान्वयन से मेल नहीं खाता है, और यहां तक ​​कि अगर मैं ठीक करता हूं कि यह ()बाहरी लाइन के पास 'अपेक्षित अयोग्य-आईडी' कहता है । आपकी पुस्तक और दृश्य स्टूडियो दोनों ही गलत हैं, अधिक मानक अनुरूप संकलक का उपयोग करने की कोशिश करें जैसे कि जी ++ या क्लैंग और आप समस्या देखेंगे।
दानी

जवाबों:


181

आपको केवल एक टेम्पलेट को तत्काल नहींextern template करने के लिए संकलक को मजबूर करने के लिए उपयोग करना चाहिए जब आप जानते हैं कि यह कहीं और तत्काल किया जाएगा। इसका उपयोग संकलन समय और ऑब्जेक्ट फ़ाइल आकार को कम करने के लिए किया जाता है।

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

// header.h

template<typename T>
void ReallyBigFunction()
{
    // Body
}

// source1.cpp

#include "header.h"
void something1()
{
    ReallyBigFunction<int>();
}

// source2.cpp

#include "header.h"
void something2()
{
    ReallyBigFunction<int>();
}

इसका परिणाम निम्न ऑब्जेक्ट फ़ाइलों में होगा:

source1.o
    void something1()
    void ReallyBigFunction<int>()    // Compiled first time

source2.o
    void something2()
    void ReallyBigFunction<int>()    // Compiled second time

यदि दोनों फाइलें एक साथ जुड़ी हुई हैं, तो एक void ReallyBigFunction<int>()को छोड़ दिया जाएगा, जिसके परिणामस्वरूप समय और ऑब्जेक्ट फ़ाइल आकार बर्बाद हो जाएगा।

समय और ऑब्जेक्ट फ़ाइल आकार को संकलित नहीं करने के लिए, एक externकीवर्ड है जो कंपाइलर को टेम्पलेट फ़ंक्शन को संकलित नहीं करता है। आपको इसका उपयोग करना चाहिए यदि और केवल आपको पता है कि इसका उपयोग उसी बाइनरी में कहीं और किया जाता है।

इसमें परिवर्तन source2.cpp:

// source2.cpp

#include "header.h"
extern template void ReallyBigFunction<int>();
void something2()
{
    ReallyBigFunction<int>();
}

निम्न ऑब्जेक्ट फ़ाइलों में परिणाम होगा:

source1.o
    void something1()
    void ReallyBigFunction<int>() // compiled just one time

source2.o
    void something2()
    // No ReallyBigFunction<int> here because of the extern

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

इसे केवल किसी प्रोजेक्ट के भीतर उपयोग किया जाना चाहिए, जैसे कि जब आप vector<int>कई बार टेम्पलेट का उपयोग करते हैं, तो आपको externसभी लेकिन एक स्रोत फ़ाइल में उपयोग करना चाहिए ।

यह एक के रूप में कक्षाओं और फ़ंक्शन पर भी लागू होता है, और यहां तक ​​कि टेम्पलेट सदस्य कार्यों के लिए भी।


2
@codekiddy: मेरे पास कोई सुराग नहीं है कि विज़ुअल स्टूडियो का क्या मतलब है। यदि आप सही काम करने के लिए सबसे अधिक ++ 11 कोड चाहते हैं, तो आपको वास्तव में अधिक आज्ञाकारी संकलक का उपयोग करना चाहिए।
दानी

4
@ डैनी: मैंने अब तक जो भी एक्सटर्नल टेंपलेट पढ़े हैं, उनमें से सबसे अच्छा विवरण!
पिएत्रो

90
"यदि आप जानते हैं कि इसका उपयोग उसी बाइनरी में कहीं और किया जाता है।" यह न तो पर्याप्त है और न ही आवश्यक है। आपका कोड "बीमार-गठन, कोई नैदानिक ​​आवश्यक नहीं है"। आपको किसी अन्य टीयू के निहित तात्कालिकता पर भरोसा करने की अनुमति नहीं है (संकलक को इसे दूर अनुकूलित करने की अनुमति है, बहुत कुछ इनलाइन फ़ंक्शन की तरह)। एक अन्य टीयू में एक स्पष्ट तात्कालिकता प्रदान की जानी चाहिए।
जोहान्स स्काउब -

32
मैं यह बताना चाहता हूं कि यह उत्तर शायद गलत है, और मैं इससे कन्नी काट गया। सौभाग्य से जोहान्स की टिप्पणी में कई वोट थे और मैंने इस बार इस पर अधिक ध्यान दिया। मैं केवल यह मान सकता हूं कि इस सवाल पर अधिकांश मतदाताओं ने वास्तव में कई संकलन इकाइयों (जैसे मैंने आज किया) में इस प्रकार के टेम्पलेट्स को लागू नहीं किया है ... कम से कम क्लैंग के लिए, इन टेम्पलेट परिभाषाओं को लगाने का एकमात्र निश्चित तरीका है आपका हेडर! चेतावनी दी!
स्टीवन लू

6
@ जोहान्सचैब-लिट, क्या आप थोड़ा और विस्तृत कर सकते हैं या शायद बेहतर जवाब दे सकते हैं? मुझे यकीन नहीं है कि अगर मैं पूरी तरह से आपकी आपत्तियों को समझ गया।
और्री

48

विकिपीडिया का सबसे अच्छा वर्णन है

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

C ++ 03 में इस सिंटैक्स को एक टेम्पलेट को तुरंत संकलित करने के लिए कंपाइलर को उपकृत करना है:

  template class std::vector<MyClass>;

C ++ 11 अब यह सिंटैक्स प्रदान करता है:

  extern template class std::vector<MyClass>;

जो संकलक को इस अनुवाद इकाई में टेम्प्लेट को तुरंत नहीं बताने के लिए कहता है।

चेतावनी: nonstandard extension used...

Microsoft VC ++ में पहले से ही कुछ वर्षों के लिए (C ++ 03 में) इस सुविधा का एक गैर-मानक संस्करण हुआ करता था । कंपाइलर कोड के साथ पोर्टेबिलिटी के मुद्दों को रोकने के लिए चेतावनी देता है जिसे अलग-अलग कंपाइलरों पर भी संकलन करने की आवश्यकता होती है।

लिंक किए गए पृष्ठ में नमूना देखें कि यह उसी तरह से काम करता है। आप एक ही समय में अन्य गैर-मानक संकलक एक्सटेंशन का उपयोग करते समय, संदेश को छोड़कर MSVC के भविष्य के संस्करणों के साथ दूर जाने की उम्मीद कर सकते हैं ।


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

4
"... जो संकलक को इस अनुवाद इकाई में टेम्पलेट को तुरंत नहीं बताने के लिए कहता है ।" मुझे नहीं लगता कि यह सच है। वर्ग परिभाषा में परिभाषित कोई भी विधि इनलाइन के रूप में गिना जाता है, इसलिए यदि एसटीएल कार्यान्वयन इनलाइन विधियों का उपयोग करता है std::vector(बहुत यकीन है कि वे सभी करते हैं), externकोई प्रभाव नहीं है।
एंड्रियास हैफेरबर्ग

हां, यह जवाब भ्रामक है। MSFT doc: "विशेषज्ञता में एक्सटर्नल कीवर्ड केवल क्लास के निकाय के बाहर परिभाषित सदस्य फ़ंक्शन पर लागू होता है। क्लास डिक्लेरेशन के अंदर परिभाषित फ़ंक्शंस इनलाइन फ़ंक्शंस माने जाते हैं और हमेशा त्वरित होते हैं।" वीएस में सभी एसटीएल कक्षाएं (आखिरी बार 2017 की जांच की गई) दुर्भाग्य से केवल इनलाइन तरीके हैं।
0kats

वे जहां भी उठते हैं, सभी इनलाइन घोषणाओं के लिए जाते हैं, हमेशा @ 0kcats
sehe

@sehe std :: वेक्टर उदाहरण के साथ विकी का संदर्भ और इसी उत्तर में MSVC का एक संदर्भ यह मानता है कि बाहरी स्टैड का उपयोग करने में कुछ लाभ हो सकता है :: MSVC में वेक्टर, जबकि अभी तक ऐसा नहीं है। सुनिश्चित नहीं हैं कि यदि यह मानक की आवश्यकता है, तो हो सकता है कि अन्य संकलक के पास एक ही मुद्दा हो।
0kcats 8

7

extern template केवल तभी आवश्यक है जब टेम्पलेट घोषणा पूर्ण हो

यह अन्य उत्तरों में संकेत दिया गया था, लेकिन मुझे नहीं लगता कि इसके लिए पर्याप्त जोर दिया गया था।

इसका मतलब यह है कि ओपी उदाहरणों में, extern templateइसका कोई प्रभाव नहीं है क्योंकि हेडर पर टेम्पलेट परिभाषाएं अधूरी थीं:

  • void f();: सिर्फ घोषणा, कोई शरीर नहीं
  • class foo: विधि की घोषणा करता है f()लेकिन उसकी कोई परिभाषा नहीं है

इसलिए मैं केवल extern templateउस विशेष मामले में परिभाषा को हटाने की सिफारिश करूंगा : आपको केवल उन्हें जोड़ना होगा यदि कक्षाएं पूरी तरह से परिभाषित हैं।

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

TemplHeader.h

template<typename T>
void f();

TemplCpp.cpp

template<typename T>
void f(){}

// Explicit instantiation for char.
template void f<char>();

main.cpp

#include "TemplHeader.h"

// Commented out from OP code, has no effect.
// extern template void f<T>(); //is this correct?

int main() {
    f<char>();
    return 0;
}

संकलन और देखने के प्रतीकों के साथ nm:

g++ -std=c++11 -Wall -Wextra -pedantic -c -o TemplCpp.o TemplCpp.cpp
g++ -std=c++11 -Wall -Wextra -pedantic -c -o Main.o Main.cpp
g++ -std=c++11 -Wall -Wextra -pedantic -o Main.out Main.o TemplCpp.o
echo TemplCpp.o
nm -C TemplCpp.o | grep f
echo Main.o
nm -C Main.o | grep f

उत्पादन:

TemplCpp.o
0000000000000000 W void f<char>()
Main.o
                 U void f<char>()

और फिर man nmहम देखते हैं कि Uइसका मतलब अपरिभाषित है, इसलिए परिभाषा केवल TemplCppवांछित के रूप में बनी रही।

यह सब पूरा हेडर घोषणाओं के व्यापार के लिए उबलता है:

  • तेजी:
    • बाहरी कोड को नए प्रकार के साथ हमारे टेम्पलेट का उपयोग करने की अनुमति देता है
    • यदि हमारे पास ऑब्जेक्ट ब्लोट के साथ ठीक है, तो हमें स्पष्ट तात्कालिकता नहीं जोड़ने का विकल्प है
  • कमियां:
    • जब उस वर्ग को विकसित किया जा रहा है, तो हेडर कार्यान्वयन परिवर्तन सभी बिल्डरों को फिर से बनाने के लिए स्मार्ट बिल्ड सिस्टम का नेतृत्व करेगा, जो कई फाइलें हो सकती हैं
    • अगर हम ऑब्जेक्ट फ़ाइल ब्लोट से बचना चाहते हैं, तो हमें न केवल स्पष्ट इंस्टेंटिसेशन करने की आवश्यकता है (उसी तरह अधूरे हेडर घोषणाओं के साथ), बल्कि extern templateहर शामिलकर्ता पर भी जोड़ दें, जो प्रोग्रामर संभवतः करने की भूल करेंगे

इसके आगे के उदाहरण यहां दिए गए हैं: स्पष्ट टेम्पलेट तात्कालिकता - इसका उपयोग कब किया जाता है?

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

और उस स्थिति में, मैं सबसे पहले निर्माण समय की समस्या से बचने के लिए बहुरूपता का उपयोग करने की कोशिश करूंगा, और केवल टेम्पलेट्स का उपयोग कर सकता हूं यदि ध्यान देने योग्य प्रदर्शन लाभ हो सकता है।

उबंटू 18.04 में परीक्षण किया गया।


4

टेम्प्लेट के साथ ज्ञात समस्या कोड ब्लोटिंग है, जो प्रत्येक और हर मॉड्यूल में क्लास की परिभाषा उत्पन्न करने का परिणाम है जो क्लास टेम्प्लाइजेशन को लागू करता है। इसे रोकने के लिए, C ++ 0x से शुरू करके, कोई व्यक्ति क्लास टेम्पलेट विशेषज्ञता के सामने कीवर्ड एक्सटर्नल का उपयोग कर सकता है

#include <MyClass>
extern template class CMyClass<int>;

टेम्प्लेट क्लास का स्पष्ट तात्पर्य केवल एक ही अनुवाद इकाई में होना चाहिए, जो टेम्प्लेट की परिभाषा के साथ बेहतर हो (MyClass.cpp)

template class CMyClass<int>;
template class CMyClass<float>;

0

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

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