हेडर में C ++ इनलाइन फ़ंक्शन क्यों हैं?


120

एनबी यह एक सवाल नहीं है कि इनलाइन फ़ंक्शन का उपयोग कैसे करें या वे कैसे काम करते हैं, अधिक क्यों वे जिस तरह से किए जाते हैं।

वर्ग सदस्य फ़ंक्शन की घोषणा के रूप में एक फ़ंक्शन को परिभाषित करने की आवश्यकता नहीं है inline, यह केवल फ़ंक्शन का वास्तविक कार्यान्वयन है। उदाहरण के लिए, शीर्ष लेख फ़ाइल में:

struct foo{
    void bar(); // no need to define this as inline
}

तो क्यों एक वर्ग के इनलाइन कार्यान्वयन काम करता है हेडर फाइल में होना करने के लिए? मैं इनलाइन फ़ंक्शन .cppफ़ाइल को क्यों नहीं डाल सकता हूं ? अगर मैं .cppफ़ाइल में इनलाइन परिभाषा डालने की कोशिश कर रहा था, तो मुझे इसकी तर्ज पर एक त्रुटि मिलेगी:

error LNK2019: unresolved external symbol 
"public: void __thiscall foo::bar(void)"
(?bar@foo@@QAEXXZ) referenced in function _main 
1>C:\Users\Me\Documents\Visual Studio 2012\Projects\inline\Debug\inline.exe 
: fatal error LNK1120: 1 unresolved externals



@ दोस्तों मैं कहूंगा कि दूसरा लिंक मेरे समान है, लेकिन मैं तर्क के बारे में अधिक पूछ रहा हूं कि इनलाइन किस तरह से काम करती है।
Thecoshman

2
उस स्थिति में, मुझे लगता है कि आपको "इनलाइन" या "हेडर फाइलें" गलत समझी जा सकती हैं; आपका कोई भी कथन सत्य नहीं है। आपके पास सदस्य फ़ंक्शन का इनलाइन कार्यान्वयन हो सकता है और आप हेडर फ़ाइल में इनलाइन फ़ंक्शन परिभाषाएँ रख सकते हैं, यह सिर्फ एक अच्छा विचार नहीं हो सकता है। क्या आप अपना प्रश्न स्पष्ट कर सकते हैं?
सीबी बेली

पोस्ट संपादित करें, मुझे लगता है कि आप उन स्थितियों के बारे में पूछ सकते हैं inlineजो एक परिभाषा पर प्रकट होती हैं लेकिन पूर्व घोषणा बनाम इसके विपरीत नहीं । यदि ऐसा है, तो यह मदद कर सकता है: stackoverflow.com/questions/4924912/…
CB Bailey

जवाबों:


122

किसी inlineफ़ंक्शन की परिभाषा को हेडर फ़ाइल में होना आवश्यक नहीं है, लेकिन इनलाइन फ़ंक्शन के लिए एक परिभाषा नियम ( ODR ) के कारण, फ़ंक्शन के लिए एक समान परिभाषा हर अनुवाद इकाई में मौजूद होनी चाहिए जो इसका उपयोग करती है।

इसे हासिल करने का सबसे आसान तरीका है कि आप हेडर फाइल में परिभाषा डाल दें।

यदि आप किसी एकल स्रोत फ़ाइल में फ़ंक्शन की परिभाषा रखना चाहते हैं तो आपको इसे घोषित नहीं करना चाहिए inline। एक फ़ंक्शन घोषित inlineनहीं होने का मतलब यह नहीं है कि कंपाइलर फ़ंक्शन को इनलाइन नहीं कर सकता है।

चाहे आपको कोई फ़ंक्शन घोषित करना चाहिए inlineया नहीं आमतौर पर एक विकल्प होता है कि आपको एक परिभाषा के नियमों के आधार पर बनाना चाहिए जो आपके लिए पालन करने के लिए सबसे अधिक समझ में आता है; जोड़ने inlineऔर फिर बाद की बाधाओं द्वारा प्रतिबंधित किया जाना थोड़ा समझ में आता है।


लेकिन संकलक .cpp फ़ाइल को संकलित नहीं करता है, जिसमें .h फाइलें शामिल होती हैं ... ताकि जब वह एक .cpp फ़ाइल संकलित करे तो उसमें दोनों डेक्लेरेशन के साथ-साथ स्रोत फ़ाइलें भी हों। खींची गई अन्य हेडर फाइलें सिर्फ इतनी हैं कि संकलक 'विश्वास' कर सकता है कि वे कार्य मौजूद हैं और किसी अन्य स्रोत फ़ाइल में कार्यान्वित किए जाएंगे
theCoshman

1
यह वास्तव में मेरा, +1मुझसे बेहतर जवाब है !
sbi

2
@ वेकेशमन: दो भेद हैं। स्रोत फ़ाइल बनाम हेडर फ़ाइल। कन्वेंशन द्वारा, हेडर फ़ाइल आमतौर पर एक स्रोत फ़ाइल को संदर्भित करती है जो कि अनुवाद इकाई के लिए आधार नहीं है, लेकिन अन्य स्रोत फ़ाइलों से केवल #included है। फिर डिक्लेरेशन बनाम परिभाषा है। आप हेडर फ़ाइलों या 'सामान्य' स्रोत फ़ाइलों में फ़ंक्शन की घोषणा या परिभाषा कर सकते हैं। मुझे डर है कि मुझे यकीन नहीं है कि आप अपनी टिप्पणी में क्या पूछ रहे हैं।
सीबी बेली

चिंता न करें, मुझे लगता है कि यह अब क्यों है ... हालांकि मुझे यकीन नहीं है कि वास्तव में इसका जवाब किसने दिया। तुम्हारा और @ ज़ानाटोस के जवाब के संयोजन ने इसे मेरे लिए समझाया।
thecoshman 12

113

इसे देखने के दो तरीके हैं:

  1. इनलाइन फ़ंक्शन को हेडर में परिभाषित किया गया है, क्योंकि फ़ंक्शन कॉल को इनलाइन करने के लिए, कंपाइलर को फ़ंक्शन बॉडी को देखने में सक्षम होना चाहिए। एक भोले संकलक के लिए, फ़ंक्शन बॉडी को कॉल के रूप में एक ही अनुवाद इकाई में होना चाहिए। (एक आधुनिक संकलक अनुवाद इकाइयों के पार अनुकूलन कर सकता है, और इसलिए फ़ंक्शन कॉल अलग हो सकती है भले ही फ़ंक्शन परिभाषा एक अलग अनुवाद इकाई में हो, लेकिन ये अनुकूलन महंगे हैं, हमेशा सक्षम नहीं होते हैं, और हमेशा समर्थित नहीं होते थे संकलक)

  2. शीर्ष लेख में परिभाषित कार्यों को चिह्नित किया जाना चाहिए inlineक्योंकि अन्यथा, हर अनुवाद इकाई में हेडर शामिल होता है जिसमें फ़ंक्शन की परिभाषा होती है, और लिंकर कई परिभाषाओं (एक परिभाषा नियम का उल्लंघन) के बारे में शिकायत करेगा। inlineकीवर्ड इस को दबा, कई अनुवाद इकाइयों (समान) परिभाषाओं को शामिल करने की इजाजत दी।

दो स्पष्टीकरण वास्तव में इस तथ्य को उबालते हैं कि inlineकीवर्ड वास्तव में वह नहीं करता है जो आप उम्मीद करेंगे।

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

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


23

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


ओह मैं समझा! हाँ, इसके वर्ग के लिए यह स्वयं इनलाइन फ़ंक्शन का उपयोग करता है, इसके अन्य कोड जो इनलाइन फ़ंक्शन का उपयोग करता है। वे केवल उस वर्ग के लिए हेडर फ़ाइल को देखते हैं जो इनलेट हो रहा है!
Thecoshman

11
मैं इस जवाब से असहमत हूं, यह सी ++ संकलक सीमा नहीं है; यह विशुद्ध रूप से है कि भाषा के नियम कैसे निर्दिष्ट किए जाते हैं। भाषा के नियम एक सरल संकलन मॉडल की अनुमति देते हैं लेकिन वे वैकल्पिक कार्यान्वयन को प्रतिबंधित नहीं करते हैं।
सीबी बेली

3
मैं @Charles से सहमत हूं। वास्तव में संकलक होते हैं जो अनुवाद इकाइयों में इनलाइन कार्य करते हैं, इसलिए यह निश्चित रूप से संकलक सीमाओं के कारण नहीं है।
sbi

5
जब भी इस उत्तर में कुछ तकनीकी त्रुटियां होती हैं, तो इससे मुझे यह देखने में मदद मिली कि हेडर फ़ाइलों और इस तरह से कंपाइलर कैसे काम करता है।
thecoshman

10

कारण यह है कि संकलक को वास्तव में कॉल के स्थान पर इसे छोड़ने में सक्षम होने के लिए परिभाषा को देखना होगा ।

याद रखें कि सी और सी ++ एक बहुत ही सरल संकलन मॉडल का उपयोग करते हैं, जहां कंपाइलर हमेशा एक समय में केवल एक अनुवाद इकाई देखता है। (यह निर्यात के लिए विफल है, जो मुख्य कारण केवल एक विक्रेता ने वास्तव में इसे लागू किया है।)


9

C ++ inlineकीवर्ड भ्रामक है, इसका मतलब यह नहीं है कि "इस फ़ंक्शन को इनलाइन करें"। यदि किसी फ़ंक्शन को इनलाइन के रूप में परिभाषित किया गया है, तो इसका सीधा मतलब है कि इसे कई बार परिभाषित किया जा सकता है जब तक कि सभी परिभाषाएं समान हों। यह inlineएक वास्तविक फ़ंक्शन के रूप में चिह्नित फ़ंक्शन के लिए पूरी तरह से कानूनी है जिसे उस बिंदु पर कोड इनलाइन होने के बजाय कहा जाता है जहां इसे कहा जाता है।

टेम्प्लेट फ़ाइल में फ़ंक्शन को परिभाषित करना टेम्प्लेट के लिए आवश्यक है, जैसे कि एक टेम्पल क्लास वास्तव में एक क्लास नहीं है, यह एक क्लास के लिए एक टेम्प्लेट है जिसमें आप कई प्रकार के बदलाव कर सकते हैं। संकलक के लिए उदाहरण के लिए जब आप एक फू वर्ग बनाने के लिए फू टेम्पलेट का उपयोग करते हैं, तो एक Foo<int>::bar()फ़ंक्शन बनाने में सक्षम होना चाहिए , वास्तविक परिभाषा दिखाई देनी चाहिए।Foo<T>::bar()


और क्योंकि यह एक है एक वर्ग के लिए टेम्पलेट है, यह एक नहीं बुलाया जाता है टेम्पलेट वर्ग एक, लेकिन वर्ग टेम्पलेट
sbi

4
पहला पैराग्राफ पूरी तरह से सही है (और मैं चाहता हूं कि मैं "भ्रामक" पर जोर दे सकता हूं), लेकिन मुझे गैर सीक्वेटुर की आवश्यकता नहीं है।
थॉमस एडल्सन

कुछ संकलक इसे एक संकेत के रूप में उपयोग करेंगे कि फ़ंक्शन संभवतः इनबिल्ड हो सकता है, लेकिन वास्तव में, यह केवल इन-इन होने की गारंटी नहीं है क्योंकि आप इसे घोषित करते हैं inline(न ही यह घोषणा करते हैं inlineकि यह इनलेट नहीं होगा)।
कीथ एम

4

मुझे पता है कि यह एक पुराना धागा है लेकिन मुझे लगा कि मुझे उस externकीवर्ड का उल्लेख करना चाहिए । मैं हाल ही में इस मुद्दे में भाग गया और निम्नानुसार हल किया है

Helper.h

namespace DX
{
    extern inline void ThrowIfFailed(HRESULT hr);
}

Helper.cpp

namespace DX
{
    inline void ThrowIfFailed(HRESULT hr)
    {
        if (FAILED(hr))
        {
            std::stringstream ss;
            ss << "#" << hr;
            throw std::exception(ss.str().c_str());
        }
    }
}

6
जब तक आप पूरी तरह से प्रोग्राम ऑप्टिमाइज़ेशन (WPO) का उपयोग नहीं कर रहे हैं, यह वास्तव में फ़ंक्शन में परिणामी नहीं जा रहा है।
चॉक वाल्बर्न

3

क्योंकि उन्हें इनलाइन करने के लिए कंपाइलर को उन्हें देखना होगा। और हेडर फाइलें "घटक" हैं जो आमतौर पर अन्य अनुवाद इकाइयों में शामिल होती हैं।

#include "file.h"
// Ok, now me (the compiler) can see the definition of that inline function. 
// So I'm able to replace calls for the actual implementation.

1

इनलाइन कार्य

C ++ में एक मैक्रो इनलाइन फ़ंक्शन के अलावा और कुछ नहीं है। इसलिए अब मैक्रोज़ संकलक के नियंत्रण में हैं।

  • महत्वपूर्ण : यदि हम किसी फ़ंक्शन को कक्षा के अंदर परिभाषित करते हैं तो यह स्वचालित रूप से इनलाइन हो जाएगा

इनलाइन फ़ंक्शन के कोड को उस स्थान पर प्रतिस्थापित किया जाता है जिसे इसे कहा जाता है, इसलिए यह कॉलिंग फ़ंक्शन के ओवरहेड को कम करता है।

कुछ मामलों में फ़ंक्शन का इनलाइनिंग काम नहीं कर सकता है, जैसे कि

  • यदि इनलाइन फंक्शन के अंदर स्टैटिक वेरिएबल का उपयोग किया जाता है।

  • यदि फ़ंक्शन जटिल है।

  • यदि फ़ंक्शन का पुनरावर्ती कॉल

  • यदि फंक्शन का पता सरलता से या खोजपूर्ण तरीके से लिया गया हो

नीचे वर्ग के रूप में परिभाषित फ़ंक्शन इनलाइन बन सकता है

inline int AddTwoVar(int x,int y); //This may not become inline 

inline int AddTwoVar(int x,int y) { return x + y; } // This becomes inline

कक्षा के अंदर परिभाषित फ़ंक्शन भी इनलाइन हो जाते हैं

// Inline SpeedMeter functions
class SpeedMeter
{
    int speed;
    public:
    int getSpeed() const { return speed; }
    void setSpeed(int varSpeed) { speed = varSpeed; }
};
int main()
{
    SpeedMeter objSM;
    objSM.setSpeed(80);
    int speedValue = A.getSpeed();
} 

यहाँ getSpeed ​​और setSpeed ​​फ़ंक्शन दोनों इनलाइन हो जाएंगे


एह, कुछ अच्छी जानकारी शायद, लेकिन वास्तव में समझाने की कोशिश क्यों नहीं करता है । शायद आप करते हैं, लेकिन अभी यह स्पष्ट नहीं कर रहे हैं।
thecoshman

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

हे टिप्पणी के लिए धन्यवाद ... नीचे C ++ micc.unifi.it/bertini/download/programmazione/… पेज 400 में सोचने से लाइनें हैं । कृपया जाँचें .. यदि आप सहमत हैं तो कृपया अपवोट करें। धन्यवाद ..... कक्षाओं के अंदर इनलाइन फ़ंक्शन को परिभाषित करने के लिए, आपको आमतौर पर इनलाइन कीवर्ड के साथ फ़ंक्शन की परिभाषा को पूर्ववर्ती होना चाहिए। हालांकि, यह एक वर्ग परिभाषा के अंदर आवश्यक नहीं है। कोई भी फ़ंक्शन जिसे आप वर्ग परिभाषा के अंदर परिभाषित करते हैं, स्वचालित रूप से एक इनलाइन है।
सौरभ राऊत

उस पुस्तक के लेखक दावा कर सकते हैं कि वे क्या चाहते हैं, क्योंकि वे किताबें लिखते हैं और कोड नहीं। यह कुछ ऐसा है जिसका मुझे गहराई से विश्लेषण करना था ताकि मेरे पोर्टेबल 3 डी डेमो को इनबिल्ट कोड से जितना संभव हो सके 64kb से कम में फिट किया जा सके। प्रोग्रामिंग तथ्यों के बारे में है और धर्म नहीं है, इसलिए यह वास्तव में कोई फर्क नहीं पड़ता है अगर कुछ "भगवान प्रोग्रामर" ने इसे एक किताब में कहा है अगर यह व्यवहार में क्या होता है इसका प्रतिनिधित्व नहीं करता है। और अधिकांश सी ++ पुस्तकें खराब सलाह का एक संग्रह हैं, जिसमें समय-समय पर आपको अपने भंडार में जोड़ने के लिए कुछ साफ-सुथरी चाल मिल जाती है।
पाब्लो एरियल

हे @PabloAriel धन्यवाद ... कृपया विश्लेषण करें और मुझे बताएं .. मैं विश्लेषण के अनुसार इस उत्तर को अद्यतन करने के लिए ठीक हूं
सौरभ राऊत
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.