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