क्या निहित तर्क रूपांतरण पर भरोसा करना खतरनाक माना जाता है?


10

C ++ में एक सुविधा है (मैं इसका सही नाम नहीं बता सकता), जो कि तर्क प्रकारों के स्वचालित रूप से कॉल करता है यदि तर्क प्रकार अपेक्षित व्यक्ति नहीं हैं।

इसका एक बहुत ही मूल उदाहरण एक फ़ंक्शन को बुला रहा है जो std::stringएक const char*तर्क के साथ अपेक्षा करता है । संकलक स्वचालित रूप से उपयुक्त std::stringनिर्माता को लागू करने के लिए कोड उत्पन्न करेगा ।

मैं सोच रहा हूं, क्या यह पठनीयता के लिए उतना ही बुरा है जितना मुझे लगता है कि यह है?

यहाँ एक उदाहरण है:

class Texture {
public:
    Texture(const std::string& imageFile);
};

class Renderer {
public:
    void Draw(const Texture& texture);
};

Renderer renderer;
std::string path = "foo.png";
renderer.Draw(path);

क्या यह ठीक है? या यह बहुत दूर जाता है? अगर मुझे ऐसा नहीं करना चाहिए, तो क्या मैं किसी तरह से क्लेंग या जीसीसी को इसके बारे में चेतावनी दे सकता हूं?


1
क्या होगा अगर ड्रा बाद में एक स्ट्रिंग संस्करण के साथ ओवरलोड था?
शाफ़्ट फ़्रीक

1
@ रेगर के उत्तर के अनुसार, मुझे नहीं लगता कि यह सभी कंपाइलरों पर संकलित होगा। उनके जवाब पर मेरी टिप्पणी देखें। जाहिरा तौर पर, सी ++ मानक के अनुसार, आप इस तरह के रूपांतरणों को श्रृंखलाबद्ध नहीं कर सकते। आप केवल एक रूपांतरण कर सकते हैं और अधिक नहीं।
जोनाथन हेंसन

क्षमा करें, वास्तव में यह संकलन नहीं था। उदाहरण अपडेट किया और यह अभी भी भयानक है, आईएमओ।
फुललिब

जवाबों:


24

इसे एक कंवर्टर कंस्ट्रक्टर (या कभी-कभी निहित कंस्ट्रक्टर या अंतर्निहित रूपांतरण) के रूप में जाना जाता है।

जब यह होता है तो मुझे चेतावनी देने के लिए एक संकलन-समय स्विच के बारे में पता नहीं है, लेकिन इसे रोकना बहुत आसान है; बस explicitकीवर्ड का उपयोग करें ।

class Texture {
public:
    explicit Texture(const std::string& imageFile);
};

कंस्ट्रक्टर्स कंवर्ट करते हैं या नहीं यह एक अच्छा विचार है: यह निर्भर करता है।

परिस्थितियाँ जिसमें निहित रूपांतरण समझ में आता है:

  • यह निर्माण करने के लिए वर्ग काफी सस्ता है अगर आपको इसका कोई मतलब नहीं है कि इसका निर्माण किया गया है।
  • कुछ वर्ग वैचारिक रूप से अपने तर्कों के समान होते हैं (जैसे कि std::stringयह उसी अवधारणा को प्रतिबिंबित करता है जैसा कि const char *यह अंतर्निहित रूप से रूपांतरित हो सकता है), इसलिए निहित रूपांतरण समझ में आता है।
  • यदि अंतर्निहित रूपांतरण अक्षम है, तो कुछ कक्षाएं बहुत अधिक अप्रिय हो जाती हैं। (स्पष्ट रूप से std :: स्ट्रिंग को हर बार जब आप स्ट्रिंग शाब्दिक पास करना चाहते हैं, तब सोचें। Boost के हिस्से समान हैं)

परिस्थितियाँ जिनमें निहित रूपांतरण कम मायने रखता है:

  • निर्माण महंगा है (जैसे कि आपका बनावट उदाहरण, जिसमें ग्राफिक फ़ाइल लोड करने और पार्स करने की आवश्यकता होती है)।
  • वर्ग अपने तर्कों के प्रति वैचारिक रूप से बहुत असंतुष्ट हैं। उदाहरण के लिए, एक सरणी-जैसे कंटेनर जो एक तर्क के रूप में अपना आकार लेता है, पर विचार करें:
    वर्ग का झंडा
    {
        फ्लैगलिस्ट (int initial_size); 
    };

    शून्य सेटफ्लैग्स (कॉस्ट फ्लैगलिस्ट और फ्लैग_लिस्ट);

    मुख्य प्रवेश बिंदु() {
        // अब यह संकलित है, भले ही यह बिल्कुल स्पष्ट नहीं है
        // यह क्या कर रहा है।
        SetFlags (42);
    }
  • निर्माण में अवांछित दुष्प्रभाव हो सकते हैं। उदाहरण के लिए, एक AnsiStringवर्ग को अंतर्निहित रूप से निर्माण नहीं करना चाहिए UnicodeString, क्योंकि यूनिकोड-टू-एएनएसआई रूपांतरण जानकारी खो सकता है।

आगे की पढाई:


3

यह एक उत्तर की तुलना में टिप्पणी का अधिक है, लेकिन एक टिप्पणी में डालने के लिए बहुत बड़ा है।

दिलचस्प है, g++मुझे ऐसा नहीं करने देता:

#include <iostream>
#include <string>

class Texture {
        public:
                Texture(const std::string& imageFile)
                {
                        std::cout << "Texture()" << std::endl;
                }
};

class Renderer {
        public:
                void Draw(const Texture& texture)
                {
                        std::cout << "Renderer.Draw()" << std::endl;
                }
};

int main(int argc, char* argv[])
{
        Renderer renderer;
        renderer.Draw("foo.png");

        return 0;
}

निम्नलिखित का उत्पादन करता है:

$ g++ -o Conversion.exe Conversion.cpp 
Conversion.cpp: In function int main(int, char**)’:
Conversion.cpp:23:25: error: no matching function for call to Renderer::Draw(const char [8])’
Conversion.cpp:14:8: note: candidate is: void Renderer::Draw(const Texture&)

हालाँकि, अगर मैं लाइन को इसमें बदलता हूँ:

   renderer.Draw(std::string("foo.png"));

यह उस रूपांतरण का प्रदर्शन करेगा।


यह वास्तव में जी ++ में एक दिलचस्प "सुविधा" है। मुझे लगता है कि या तो एक बग है जो सही कोड उत्पन्न करने के लिए संकलन समय पर जहाँ तक संभव हो नीचे जाने के बजाय केवल एक प्रकार की गहरी जाँच करता है, या एक ध्वज है जिसे आपके g ++ कमांड में सेट करने की आवश्यकता है।
जोनाथन हेंसन

1
en.cppreference.com/w/cpp/language/implicit_cast ऐसा लगता है कि g ++ मानक का सख्ती से पालन कर रहा है। यह माइक्रोसॉफ्ट या मैक का कंपाइलर है जो ओपी कोड के साथ थोड़ा बहुत उदार है। विशेष रूप से यह कथन है: "जब एक निर्माता या एक उपयोगकर्ता-परिभाषित रूपांतरण फ़ंक्शन के तर्क पर विचार किया जाता है, तो केवल एक मानक रूपांतरण अनुक्रम की अनुमति दी जाती है (अन्यथा उपयोगकर्ता-परिभाषित रूपांतरणों को प्रभावी रूप से जंजीर बनाया जा सकता है)"
जोनाथन हेंसन

हाँ, मैंने कुछ gccसंकलक विकल्पों का परीक्षण करने के लिए कोड को एक साथ फेंक दिया (जो ऐसा नहीं लगता है कि इस विशेष विवरण को संबोधित करने के लिए कोई भी हैं)। मैं इसमें बहुत आगे नहीं लग रहा था (मैं काम करने वाला हूँ :-) लेकिन gccमानक का पालन करना और explicitकीवर्ड का उपयोग करना एक संकलक विकल्प शायद अनावश्यक समझा गया था।
डेव रैगर

अंतर्निहित रूपांतरण जंजीर नहीं हैं, और Textureसंभवतः इसका निर्माण अंतर्निहित रूप से नहीं किया जाना चाहिए (अन्य उत्तरों में दिशानिर्देशों के अनुसार), इसलिए एक बेहतर कॉल-साइट होगी renderer.Draw(Texture("foo.png"));(यह मानकर कि मैं उम्मीद के मुताबिक काम करता हूं)।
ब्लेज़ोरब्लेड

3

इसे निहित प्रकार रूपांतरण कहा जाता है। सामान्य तौर पर यह एक अच्छी बात है, क्योंकि यह अनावश्यक पुनरावृत्ति को रोकता है। उदाहरण के लिए, आपको स्वचालित रूप से इसके लिए कोई अतिरिक्त कोड लिखने std::stringके Drawबिना एक संस्करण मिलता है। यह खुले-बंद सिद्धांत का पालन करने में भी मदद कर सकता है, क्योंकि यह आपको खुद Rendererको संशोधित किए बिना क्षमताओं का विस्तार करने देता Rendererहै।

दूसरी ओर, यह कमियों के बिना नहीं है। यह पता लगाना मुश्किल हो सकता है कि एक तर्क कहां से आ रहा है, एक बात के लिए। यह कभी-कभी अन्य मामलों में अप्रत्याशित परिणाम उत्पन्न कर सकता है। यही explicitकीवर्ड के लिए है। यदि आप इसे Textureकंस्ट्रक्टर पर रखते हैं, तो यह निहित टाइप रूपांतरण के लिए उस कंस्ट्रक्टर का उपयोग करने में अक्षम करता है। मुझे विश्व स्तर पर निहित प्रकार रूपांतरण पर चेतावनी देने के लिए एक विधि के बारे में पता नहीं है, लेकिन इसका मतलब यह नहीं है कि एक विधि मौजूद नहीं है, केवल उस gcc में विकल्पों की एक बड़ी संख्या है।

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