2016-02-16 7 views
14

मेरे पास दो कक्षाएं हैं, A और B, प्रत्येक रूपांतरण को B पर परिभाषित करता है। A में एक रूपांतरण ऑपरेटर है B, B में A से एक निर्माता है। static_cast<B> पर कॉल नहीं करना चाहिए? G ++ का उपयोग करके यह कोड संकलित करता है और रूपांतरण कन्स्ट्रक्टर चुनता है।क्या यह कोड एक संदिग्ध रूपांतरण त्रुटि को फेंक नहीं देना चाहिए?

#include<iostream> 

using namespace std; 

struct B; 
struct A { 
    A(const int& n) : x(n) {} 
    operator B() const;   //this const doesn't change the output of this code 
    int x; 
}; 

struct B{ 
    B(const double& n) : x(n) {} 
    B(const A& a); 
    double x; 
}; 

A::operator B() const   //this const doesn't change the output of this code 
{ 
    cout << "called A's conversion operator" << endl; 
    return B(double(x)); 
} 

B::B(const A& a) 
{ 
    cout << "called B's conversion constructor" << endl; 
    x = (double) a.x; 
} 

int main() { 
    A a(10); 
    static_cast<B>(a);   // prints B's conversion constructor 
} 
+0

दिलचस्प। मैंने उस जवाब का परीक्षण करने के लिए कोड अपडेट किया है। यह जवाब इंगित करता है कि रूपांतरण ऑपरेटर को कॉल किया जाना चाहिए, जबकि यह कोड रूपांतरण कन्स्ट्रक्टर को कॉल करता है। – roro

+2

मुझे लगता है कि आप सही हैं।प्रश्न बहुत समान हैं, इसलिए वे डुप्लिक की तरह दिखते हैं, लेकिन वे नहीं हैं। –

उत्तर

13

उपयोगकर्ता परिभाषित रूपांतरण अनुक्रमों के लिए; कनवर्टिंग कन्स्ट्रक्टर और कनवर्ज़न ऑपरेटर के बीच दी गई प्राथमिकता प्रतीत नहीं होती है, वे दोनों उम्मीदवार हैं;

§13.3.3.1.2/1 उपयोगकर्ता-परिभाषित रूपांतरण दृश्यों

उपयोगकर्ता-निर्धारित रूपांतरण अनुक्रम एक प्रारंभिक मानक रूपांतरण अनुक्रम एक उपयोगकर्ता- परिभाषित रूपांतरण के बाद के होते हैं (12.3) के बाद एक दूसरा मानक रूपांतरण अनुक्रम। यदि उपयोगकर्ता द्वारा परिभाषित रूपांतरण एक निर्माता (12.3.1) द्वारा निर्दिष्ट किया गया है, प्रारंभिक मानक रूपांतरण अनुक्रम स्रोत प्रकार को कन्स्ट्रक्टर के तर्क द्वारा आवश्यक प्रकार में परिवर्तित करता है। यदि उपयोगकर्ता द्वारा परिभाषित रूपांतरण रूपांतरण फ़ंक्शन (12.3.2) द्वारा निर्दिष्ट किया गया है, तो प्रारंभिक मानक रूपांतरण अनुक्रम स्रोत प्रकार को रूपांतरण फ़ंक्शन के निहित ऑब्जेक्ट पैरामीटर में परिवर्तित करता है।

इसलिए यदि रूपांतरण किया गया था;

B b2 = a; // ambiguous? 

यह संदिग्ध हो सकता है और संकलन विफल हो सकता है। क्लैंग संकलन में विफल रहता है, जी ++ कोड स्वीकार करता है और कन्स्ट्रक्टर का उपयोग करता है; demo code, वीएस कोड भी स्वीकार करता है। वीएस और जी ++ कनवर्टिंग कन्स्ट्रक्टर को कॉल करें (ओपी कोड के अनुसार)।

पोस्ट कोड के विचाराधीन, उपयोगकर्ता ने रूपांतरण अनुक्रमों (कन्स्ट्रक्टर और कनवर्टिंग ऑपरेटर द्वारा) परिभाषित किया और static_cast के उपयोग पर विचार करने की आवश्यकता है।

§5.2.9/4 स्टेटिक डाली

कोई व्यंजक ई स्पष्ट रूप से कुछ का आविष्कार के लिए, अगर घोषणा T t(e); अच्छी तरह से बनाई है एक प्रकार T रूप static_cast<T>(e) के static_cast उपयोग करने के लिए परिवर्तित किया जा सकता अस्थायी चर t (8.5)। इस तरह के एक स्पष्ट रूपांतरण का प्रभाव घोषणा और प्रारंभिक प्रदर्शन के रूप में और फिर रूपांतरण के परिणामस्वरूप अस्थायी चर का उपयोग करने जैसा ही है। अभिव्यक्ति e का उपयोग एक glvalue के रूप में किया जाता है यदि केवल प्रारंभिकता इसे एक lvalue के रूप में उपयोग करता है।

उपर्युक्त उद्धरण से, static_castB temp(a); के बराबर है और इस प्रकार, प्रत्यक्ष initialisation अनुक्रम प्रयोग किया जाता है।

§13.3.1.3/1 निर्माता द्वारा प्रारंभ

जब वर्ग प्रकार की वस्तुओं डायरेक्ट-प्रारंभ कर रहे हैं (8.5), एक ही या एक व्युत्पन्न वर्ग प्रकार की अभिव्यक्ति से कॉपी-प्रारंभ (8.5), या डिफ़ॉल्ट-प्रारंभिक (8.5), ओवरलोड रिज़ॉल्यूशन कन्स्ट्रक्टर का चयन करता है।प्रत्यक्ष-प्रारंभिकरण या डिफ़ॉल्ट-प्रारंभिकरण के लिए, उम्मीदवार कार्य प्रारंभ होने वाले ऑब्जेक्ट के वर्ग के सभी निर्माता हैं। प्रति-प्रारंभिकरण के लिए, उम्मीदवार कार्य उस वर्ग के सभी कनवर्ट करने वाले कन्स्ट्रक्टर (12.3.1) हैं। तर्क सूची प्रारंभकर्ता की अभिव्यक्ति-सूची या असाइनमेंट-अभिव्यक्ति है।

सामान्य में (explicit और const चिंताओं के रूप में चिह्नित किसी भी निर्माणकर्ता और ऑपरेटरों को छोड़कर), B(const A& a); निर्माता और एक A से एक B के निर्माण को देखते हुए, क्योंकि यह जब best viable function पर विचार सटीक मिलान प्रदान करता है निर्माता जीतने पर ; चूंकि implicit conversions की आवश्यकता नहीं है (§13.3; ओवरलोड रिज़ॉल्यूशन)।


तो निर्माता B(const A& a); हटा दिया गया था, रूपांतरण (static_cast<> साथ अभी भी सफल होने के बाद से उपयोगकर्ता परिभाषित रूपांतरण ऑपरेटर एक उम्मीदवार है और इसके उपयोग अस्पष्ट नहीं है।

§13.3.1.4/1 उपयोगकर्ता परिभाषित रूपांतरण

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

उद्धरण C++ मानक के N4567 ड्राफ्ट से लिया जाता है।


यह भी सिर्फ एक वस्तु है, यानी एक विधि बुला के निर्माण के बाहर एक उपयोगकर्ता-निर्धारित रूपांतरण अनुक्रम आह्वान करने के लिए शिक्षाप्रद होगा।

कोड सूची (और ऊपर नियम) को देखते हुए;

#include <iostream> 
using namespace std; 
struct A; 
struct B { 
    B() {} 
    B(const A&) { cout << "called B's conversion constructor" << endl; } 
}; 
struct A { 
    A() {} 
    operator B() const { cout << "called A's conversion operator" << endl; return B(); } 
}; 
void func(B) {} 
int main() { 
    A a; 
    B b1 = static_cast<B>(a); // 1. cast 
    B b2 = a; // 2. copy initialise 
    B b3 (a); // 3. direct initialise 
    func(a); // 4. user defined conversion 
} 

बजना, जी ++ (demo) और वी.एस. अलग परिणाम और अनुपालन के इस प्रकार संभवतः विभिन्न स्तरों प्रदान करते हैं।

  • बजना विफल रहता है 2. और 4.
  • जी ++ स्वीकार करता है 3. के माध्यम से 1. 4. के माध्यम से
  • वी.एस. विफल रहता है 4.
उपरोक्त नियमों से

, 1. सभी के बाद से सफल होने चाहिए B कन्वर्टिंग कन्स्ट्रक्टर एक उम्मीदवार है और इसके लिए कोई और उपयोगकर्ता रूपांतरण की आवश्यकता नहीं है; उन निर्माणों के लिए प्रत्यक्ष निर्माण और प्रति प्रारंभिकरण का उपयोग किया जाता है। मानक से पढ़ना (ऊपर दिए गए अंश, विशेष रूप से §13.3.3.1.2/1 और §13.3.1.4/1, और फिर §8.5/17.6.2), 2. और 4. विफल/विफल होना चाहिए और संदिग्ध होना चाहिए - चूंकि रूपांतरण कन्स्ट्रक्टर और रूपांतरण ऑपरेटर को कोई स्पष्ट आदेश नहीं माना जा रहा है।

मेरा मानना ​​है कि यह एक अनपेक्षित उपयोग केस हो सकता है (इस तरह से एक दूसरे को बदलने में सक्षम होने के प्रकार; वहां एक तर्क है कि एक रूपांतरण अनुक्रम सामान्य उपयोग केस होगा)।

+0

मुझे लगता है कि 2 और 4 विफल होना चाहिए, क्योंकि वे * प्रति-प्रारंभिक * हैं (लेकिन एक ही प्रकार की अभिव्यक्ति से नहीं - इसलिए [over.match.copy] द्वारा कवर किया गया है, [over.match.ctor] नहीं) और वहां दो उपयोगकर्ता परिभाषित अनुक्रम हैं। (दो उपयोगकर्ता-परिभाषित अनुक्रम हमेशा संदिग्ध होते हैं जब तक यह अलग-अलग रैंक किए गए मानक रूपांतरणों के साथ संयुक्त उपयोगकर्ता-परिभाषित रूपांतरण नहीं होते हैं) –

+0

@MM संभव है, लेकिन [over.match.ctor] स्पष्ट रूप से कहता है "प्रति-प्रारंभिकरण के लिए, उम्मीदवार कार्य उस वर्ग के सभी कनवर्टिंग कन्स्ट्रक्टर (12.3.1) "मुझे लगता है कि दोनों व्याख्याओं को लागू किया जा सकता है। – Niall

+0

[over.match.ctor] कहता है "एक ही या व्युत्पन्न वर्ग प्रकार की अभिव्यक्ति से प्रतिलिपि बनाई गई"। (शेष अनुच्छेद केवल पहली वाक्य द्वारा चुने गए मामलों पर लागू होता है) –

संबंधित मुद्दे