2016-06-18 6 views
10

दो संकलन इकाइयों के साथ निम्नलिखित प्रोग्राम पर विचार करें।हेडर में स्ट्रिंग्स - क्या यह ओडीआर का उल्लंघन करता है?


// a.hpp 

class A { 
    static const char * get() { return "foo"; } 
}; 

void f(); 

// a.cpp 

#include "a.hpp" 
#include <iostream> 

void f() { 
    std::cout << A::get() << std::endl; 
} 

// main.cpp 

#include "a.hpp" 
#include <iostream> 

void g() { 
    std::cout << A::get() << std::endl; 
} 

int main() { 
    f(); 
    g(); 
} 

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

मैं धारणा है कि यह कर के इस तरह से (ऊपर दिखाया गया है) एक समारोह के साथ था "ठीक", क्योंकि यह एक inline समारोह है और लिंकर कोई डुप्लीकेट प्रतियां कि उत्पादित कर रहे हैं समाप्त के तहत किया गया था, और कार्यक्रमों इस का उपयोग करते हुए लिखा पैटर्न ठीक काम करने लगते हैं। हालांकि, अब मुझे संदेह है कि यह वास्तव में वैध है या नहीं।

फ़ंक्शन A::get दो अलग-अलग अनुवाद इकाइयों में ओडीआर-प्रयुक्त होता है, लेकिन यह निश्चित रूप से इनलाइन है क्योंकि यह कक्षा सदस्य है।

[basic.def.odr.6] में यह कहा गया है:

वहाँ बाहरी लिंकेज (7.1.2) के साथ एक ... इनलाइन समारोह के एक से अधिक परिभाषा हो सकता है ... कि प्रत्येक परिभाषा प्रदान की एक कार्यक्रम में प्रकट होता है एक अलग अनुवाद इकाई में, और प्रदान की गई परिभाषा निम्नलिखित आवश्यकताओं को पूरा करती है। को D नामक ऐसी इकाई को एक से अधिक अनुवाद इकाई में परिभाषित किया गया है, तो
- D की प्रत्येक परिभाषा में टोकन के समान अनुक्रम शामिल होंगे; और
- D से प्रत्येक परिभाषा, इसी नाम, ऊपर देखा 3.4 के अनुसार, एक इकाई का उल्लेख करेगा परिभाषित D की परिभाषा के भीतर, या, एक ही इकाई का संदर्भ अधिभार संकल्प के बाद होगा (13.3) और मिलान के बाद आंशिक टेम्पलेट विशेषज्ञता (14.8.3), सिवाय इसके कि एक नाम गैर-अस्थिर कॉन्स्ट ऑब्जेक्ट को आंतरिक या कोई लिंकेज के साथ संदर्भित कर सकता है यदि ऑब्जेक्ट में D, की सभी परिभाषाओं में समान शाब्दिक प्रकार है और ऑब्जेक्ट प्रारंभ होता है एक निरंतर अभिव्यक्ति (5.1 9), और ऑब्जेक्ट odr-used नहीं है, और ऑब्जेक्ट का D की सभी परिभाषाओं में समान मान है; और
- D की प्रत्येक परिभाषा में, संबंधित इकाइयों में एक ही भाषा संबंध होगा; और
- D की परिभाषा इन सभी आवश्यकताओं को पूरा ... (अधिक की स्थिति है कि प्रासंगिक प्रतीत नहीं)

हैं, तो तो कार्यक्रम से व्यवहार करेगा के रूप में अगर वहाँ D की एक एकल परिभाषा थे। यदि D की परिभाषा इन आवश्यकताओं को पूरा नहीं करती है, तो व्यवहार अपरिभाषित है।

मेरे उदाहरण कार्यक्रम में, दो परिभाषाएं (प्रत्येक अनुवाद इकाई में से प्रत्येक) प्रत्येक टोकन के समान अनुक्रम से मेल खाती है। (यही कारण है कि मैंने मूल रूप से सोचा था कि यह ठीक था।)

हालांकि, यह स्पष्ट नहीं है कि दूसरी स्थिति संतुष्ट है। क्योंकि, नाम "foo" दो संकलन इकाइयों में एक ही वस्तु के अनुरूप नहीं हो सकता है - यह संभावित रूप से प्रत्येक में "अलग" स्ट्रिंग अक्षर है, नहीं?

मैं इस कार्यक्रम को बदलने की कोशिश की:

static const void * get() { return static_cast<const void*>("foo"); } 

इतना है कि यह स्ट्रिंग शाब्दिक का पता प्रिंट, और मैं एक ही पते मिलता है, लेकिन मुझे यकीन है कि क्या होने की गारंटी है, तो नहीं कर रहा हूँ।

क्या यह "... D की परिभाषा के भीतर परिभाषित एक इकाई को संदर्भित करेगा" के अंतर्गत आता है? "foo" को A::get के भीतर परिभाषित माना जाता है? ऐसा लगता है, लेकिन जैसा कि मैं अनौपचारिक रूप से समझता हूं, स्ट्रिंग अक्षर अंततः संकलक को कुछ प्रकार के वैश्विक const char[] को उत्सर्जित करने का कारण बनता है जो निष्पादन योग्य के एक विशेष खंड में रहता है। क्या वह "इकाई" A::get के भीतर माना जाता है या यह प्रासंगिक नहीं है?

"foo" को "नाम" भी माना जाता है, या शब्द "नाम" केवल एक वैध सी ++ "पहचानकर्ता" का संदर्भ देता है, जैसे किसी चर या फ़ंक्शन के लिए उपयोग किया जा सकता है? एक ओर यह कहते हैं:

[basic][3.4]
एक नाम एक पहचानकर्ता (2.11), ऑपरेटर-समारोह-आईडी (13.5), शाब्दिक-ऑपरेटर-आईडी (13.5.8), conversion- का एक प्रयोग है फ़ंक्शन-आईडी (12.3.2), या टेम्पलेट-आईडी (14.2) जो किसी इकाई या लेबल को इंगित करता है (6.6.4, 6.1)।

और पहचानकर्ता

[lex.name][2.11]
है एक पहचानकर्ता अक्षरों और अंकों का एक मनमाने ढंग से लंबे अनुक्रम है।

तो ऐसा लगता है कि एक स्ट्रिंग शाब्दिक नाम नहीं है।

दूसरी ओर खंड में 5

[expr.prim.general][5.1.1.1]
एक स्ट्रिंग शाब्दिक एक lvalue है; अन्य सभी शाब्दिक प्रावधान हैं।

आम तौर पर, मैंने सोचा कि lvalues नाम हैं।

+2

चूंकि इसे टैग किया गया है [भाषा-वकील], मैं इसे किसी ऐसे व्यक्ति द्वारा उत्तर देने के लिए छोड़ दूंगा जो प्रासंगिक स्पेस उद्धरणों को ट्रैक करेगा; लेकिन एफडब्ल्यूआईडब्ल्यू, "नाम" का अर्थ मूल रूप से "एक चीज जिसे घोषित किया जा सकता है" - जैसे, एक चर-नाम, एक फ़ंक्शन-नाम, एक वर्ग-नाम इत्यादि - और स्ट्रिंग अक्षर निश्चित रूप से * नहीं * नाम हैं। (थोड़ा और औपचारिक गणना के लिए http://en.cppreference.com/w/cpp/language/identifiers#Names देखें।) – ruakh

+0

यह एक अकादमिक प्रश्न है। वर्तमान में यह उन लोगों के लिए बहुत रूचि रखता है जो वर्तमान में नए मानक बनाते हैं, जो स्पष्ट रूप से छेद को जोड़ने के सरल समाधान के बारे में सोच नहीं सकते हैं (मुझे लगता है कि प्रक्रिया सी ++ 11 और सी ++ 14 के बीच कहीं भी फूली हुई थी)। व्यावहारिक रूप से एक 'इनलाइन' फ़ंक्शन एक विवादित लिंकर रिकॉर्ड उत्पन्न करता है, और लिंकर केवल एक एकल परिभाषा का चयन करता है, जो व्यावहारिक रूप से स्ट्रिंग की विशिष्टता की गारंटी देता है - कम से कम यदि मशीन कोड ऑप्टिमाइज़ेशन में उस व्यवहार का सम्मान करता है, जैसा कि करता है। –

+0

यदि आप विचारों को देखना चाहते हैं, जैसा कि मुझे याद है, मैंने पिछली बार इनलाइन डेटा के बारे में एक प्रस्ताव में चर्चा की थी (जहां वे छेद को प्लग करने पर विचार करने में विफल रहे थे)। –

उत्तर

7

आपका अंतिम तर्क बकवास है। "foo" व्याकरणिक रूप से एक नाम भी नहीं है, लेकिन स्ट्रिंग-शाब्दिक है। और स्ट्रिंग अक्षर लिवल हैं और नामों वाले कुछ लाइब्रेल्स का मतलब यह नहीं है कि स्ट्रिंग अक्षर या नाम हैं। आपके कोड में उपयोग किए जाने वाले स्ट्रिंग अक्षर ओडीआर का उल्लंघन नहीं करते हैं।

वास्तव में, सी ++ 11 तक, यह अनिवार्य था कि टीयू में इनलाइन फ़ंक्शंस की कई परिभाषाओं में स्ट्रिंग अक्षर एक ही इकाई को निर्दिष्ट करते हैं, लेकिन उस अधूरा और अधिकतर अनुपूरक नियम CWG 1823 द्वारा हटा दिया गया था।

वजह से, नाम "foo" दो संकलन इकाइयों में एक ही वस्तु के अनुरूप नहीं हो सकता है - यह संभवतः एक "अलग" स्ट्रिंग प्रत्येक में शाब्दिक है, कोई?

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

+0

मुझे लगता है कि नाम होने की सबसे प्रमुख विशेषता है, यह नाम संकल्प होता है। स्पष्ट रूप से, नामस्थान, छिपाने, और ऐसे, 'foo' 'पर लागू नहीं होते हैं, इसलिए मुझे यह एक बड़ा संकेत होना चाहिए कि यह एक नाम नहीं है। –

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