2010-01-11 14 views
18

स्वीकार कर रहा है क्या केवल स्ट्रिंग को शाब्दिक रूप से स्वीकार करता है, लेकिन उदाहरण के लिए नहीं। char const *?सी ++: कन्स्ट्रक्टर केवल एक स्ट्रिंग शाब्दिक

क्या दो अधिभार होना संभव है जो स्ट्रिंग अक्षर और char const * के बीच अंतर कर सकते हैं?

सी ++ 0x एक कस्टम प्रत्यय के साथ इसे अनुमति देगा - लेकिन मैं एक "पहले" समाधान की तलाश में हूं।

तर्क: तारों की ढेर प्रतिलिपि से परहेज करते हुए स्ट्रिंग अक्षर के रूप में दिए जाने पर संशोधित नहीं किया जाएगा।

ये स्ट्रिंग सीधे किसी एपीआई पर const char * किसी भी प्रसंस्करण के बिना उम्मीद कर रही हैं। अधिकतर कॉल उन अक्षरों का उपयोग करते हैं जिनके लिए कोई अतिरिक्त प्रसंस्करण की आवश्यकता नहीं होती है, केवल कुछ मामलों में उनका निर्माण होता है। मैं मूल कॉल व्यवहार को संरक्षित करने की संभावना तलाश रहा हूं।

नोट: - के बाद से यह जवाब में आता है: प्रश्न में कोड std::string बिल्कुल का उपयोग नहीं करता है, लेकिन एक अच्छा उदाहरण होगा:

class foo 
{ 
    std::string m_str; 
    char const * m_cstr;  
public: 
    foo(<string literal> s) : m_cstr(p) {} 
    foo(char const * s) : m_str(s) { m_cstr = s.c_str(); } 
    foo(std::string const & s) : m_str(s) { m_cstr = s.c_str(); } 

    operator char const *() const { return m_cstr; } 
} 

परिणाम:

(1) यह नहीं किया जा सकता है।
(2) मुझे एहसास हुआ कि मैं एक शाब्दिक भी नहीं ढूंढ रहा हूं, लेकिन एक संकलन-समय-निरंतर (यानी "कुछ भी जिसे कॉपी नहीं किया जाना चाहिए")।

मैं शायद निम्नलिखित पैटर्न के बजाय का उपयोग करेगा:

const literal str_Ophelia = "Ophelia"; 

void Foo() 
{ 
    Hamlet(str_Ophelia, ...); // can receive literal or string or const char * 
} 

एक सरल

struct literal 
{ 
    char const * data; 
    literal(char const * p) : data(p) {} 
    operator const char *() const { return data; } 
}; 

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

+0

मुझे ऐसा नहीं लगता है, क्योंकि 'स्ट्रिंग' एक निर्माता है कि लेता है एक 'चार कॉन्स * '। –

+0

क्या आप तर्क पर विस्तार कर सकते हैं? मैं काफी नहीं देख सकता कि आप एक स्ट्रिंग अक्षर की प्रतिलिपि बनाना क्यों चाहते हैं, लेकिन 'कॉन्स्ट char *' (या दूसरी तरफ) की प्रतिलिपि न लें। आपको बिल्कुल वही तरीके से बदलने की अनुमति नहीं है। – Hexagon

+0

आपको एक ध्वज बनाए रखना होगा और आपके विनाशक में सशर्त कोड होना होगा। ऐसा लगता है कि स्ट्रिंग अक्षर का उपयोग करके आपको बहुत अधिक ऑब्जेक्ट्स बनाना होगा, आमतौर पर मैं इसे सार्थक बनाने के लिए करता हूं। –

उत्तर

9

नहीं, आप बस ऐसा नहीं कर सकते हैं - स्ट्रिंग अक्षर और कॉन्स char * एक दूसरे के बदले हैं। एक वर्कअराउंड एक विशेष वर्ग को स्ट्रिंग्स को स्ट्रिंग करने के लिए पॉइंटर्स को पकड़ने और केवल कन्स्ट्रक्टर को स्वीकार करने के लिए पेश किया जा सकता है। इस तरह जब भी आपको एक शाब्दिक उत्तीर्ण करने की आवश्यकता होती है तो आप उस वर्ग के निर्माता को कॉल करते हैं और अस्थायी वस्तु को पास करते हैं। यह दुरुपयोग को पूरी तरह से रोकता नहीं है, लेकिन कोड को और अधिक रखरखाव बनाता है।

+0

क्या यह अभी भी सी ++ 14 के लिए सच है? हम्म, शायद उपयोगकर्ता परिभाषित अक्षर का उपयोग किया जा सकता है? http://en.cppreference.com/w/cpp/language/user_literal – panzi

3

यदि आप जानते हैं कि स्ट्रिंग अक्षर के साथ आपका कंपाइलर और प्लेटफार्म कैसे निपटता है, तो ऐसा समाधान लिखना संभव हो सकता है जो ऐसा कर सकता है। यदि आप जानते हैं कि आपका कंपाइलर स्ट्रिंग अक्षर को स्मृति के एक विशिष्ट क्षेत्र में हमेशा रखता है, तो आप उस स्मृति की सीमाओं के विरुद्ध सूचक की जांच कर सकते हैं। यदि यह उस ब्लॉक के भीतर आता है, तो आपको एक स्ट्रिंग अक्षर मिला है; अन्यथा आपको ढेर या ढेर पर एक स्ट्रिंग संग्रहित हो गया है।

हालांकि, यह समाधान प्लेटफ़ॉर्म/कंपाइलर-विशिष्ट होगा। यह पोर्टेबल नहीं होगा।

+0

शायद ऐसा करने का एकमात्र "सुरक्षित" तरीका है, लेकिन मैं उस बहुत ही अयोग्य चाल पर भरोसा नहीं करना चाहता हूं। कोई बात नहीं धन्यवाद! – peterchen

19

कार्य समाधान sbi idea के आधार पर:

struct char_wrapper 
{ 
    char_wrapper(const char* val) : val(val) {}; 
    const char* val; 
}; 

class MyClass { 
public: 
    template< std::size_t N > 
    explicit MyClass(const char (&str)[N]) 
    { 
     cout << "LITERAL" << endl; 
    } 
    template< std::size_t N > 
    explicit MyClass(char (&str)[N]) 
    { 
     cout << "pointer" << endl; 
    }  
    MyClass(char_wrapper m) 
    { 
    cout << "pointer" << endl; 
    } 
}; 

int main() 
{ 
    MyClass z("TEST1");  // LITERAL 
    const char* b = "fff"; 
    MyClass a(b);   // pointer 
    char tmp[256]; 
    strcpy(tmp, "hello"); 
    MyClass c(tmp);   // pointer 
} 
+2

@Downvoter: टिप्पणी करने की देखभाल? आप किससे असहमत हैं? –

+1

शायद यह तथ्य कि यह काम नहीं करता है - यह प्रारंभ के लिए स्ट्रिंग अक्षर के रूप में वर्ण सरणी को पहचानता है। –

+0

फिर भी, ओवरलोड रिज़ॉल्यूशन समस्या के आसपास काम करने के लिए goo विचार। – peterchen

0

कुछ प्लेटफार्मों पर, मैं केवल पढ़ने के लिए स्मृति से पाठ का उपयोग करने के कार्यक्रम के लिए आदेश में static const char * के रूप में स्ट्रिंग शाब्दिक घोषित करने के लिए किया है। जब const char * के रूप में घोषित किया गया, तो असेंबली सूची से पता चला कि पाठ को रोम से एक स्टैक वैरिएबल पर कॉपी किया गया था।

रिसीवर के बारे में चिंता करने के बजाय, शायद स्ट्रिंग अक्षर को static const char * के साथ घोषित करने का प्रयास करें।

0
सी ++ 14 में एक नया उपयोगकर्ता परिभाषित शाब्दिक साथ

(बजना 3.5 के लिए के रूप में - यह साथ काम करता है सी ++ 11 भी), वहाँ एक सुरुचिपूर्ण समाधान है:

class Literal { 
public: 
    explicit Literal(const char* literal) : literal_(literal) {} 
    // The constructor is public to allow explicit conversion of external string 
    // literals to `_L` literals. If there is no such need, then move constructor 
    // to private section. 

    operator const char*() { return literal_; } 

private: 
    friend Literal operator"" _L (const char*, unsigned long); 
    // Helps, when constructor is moved to private section. 

    const char* literal_; 
}; 

Literal operator"" _L (const char* str, unsigned long) { 
    return Literal(str); 
} 

ऐसा लगता है कि इस्तेमाल किया जा सकता इस:

void f1(Literal) {} // Accepts literals only. 

int main() { 
    auto str1 = "OMG! Teh Rey!"_L; 
    std::cout << str1 << std::endl; 
    f(str1); 
} 

वहाँ एक खामी है: आप हर शाब्दिक को _L संलग्न करने के लिए है - लेकिन यह एक बड़ी बात है, वास्तव में नहीं है।

17

हां, यह किया जा सकता है! मैं एक समाधान के साथ आया जो सी ++ 03 के साथ काम करता है और एक रैपर वर्ग के बिना (जो रिटर्न स्टेटमेंट में कुछ अंतर्निहित रूपांतरण तोड़ता है)।

सबसे पहले, आपको const char (&)[N] प्रकारों के लिए एक कन्स्ट्रक्टर टेम्पलेट की आवश्यकता है, क्योंकि यह स्ट्रिंग अक्षर का मूल प्रकार है। फिर आपको char (&)[N] के प्रकारों के लिए एक और की आवश्यकता है - उदाहरण के लिए चार बफर की तरह - ताकि वे साहित्यिक के लिए कन्स्ट्रक्टर में समाप्त न हों। और शायद आप const char* प्रकार के लिए एक सामान्य कन्स्ट्रक्टर भी चाहते हैं।

template<int N> Foo(const char (&)[N]); // for string literals 
template<int N> Foo(char (&)[N]);  // for non-const char arrays like buffers 
Foo(const char*);      // normal c strings 

समस्या अब है, कि स्ट्रिंग के लिए शाब्दिक संकलक अभी भी सोचता है, कि const char* निर्माता, एक टेम्पलेट उदाहरण की तुलना में एक बेहतर विकल्प है क्योंकि सरणी-टू-सूचक है सटीक मेल खाने वाला रैंक रूपांतरण । (13.3.3.1.1)

तो, यह चाल const char* कन्स्ट्रक्टर की प्राथमिकता को कम करने के लिए है। इसे इसे टेम्पलेट में बदलकर और SFINAE का उपयोग करके इसे केवल const char* के विरुद्ध मिलान करके किया जा सकता है। कन्स्ट्रक्टर के पास कोई रिटर्न वैल्यू नहीं है और केवल एक पैरामीटर है, जो कि कटौती के लिए जरूरी है। इसलिए डिफ़ॉल्ट मान के साथ एक और "डमी पैरामीटर", आवश्यक है कि सशर्त प्रकार विशेषता का उपयोग करता है: template<typename T> Foo(T, typename IsCharPtr<T>::Type=0)

समाधान:

#include <iostream> 

#define BARK std::cout << __PRETTY_FUNCTION__ << std::endl 

struct Dummy {}; 
template<typename T> struct IsCharPtr {}; 
template<> struct IsCharPtr<const char *> { typedef Dummy* Type; }; 
template<> struct IsCharPtr<char *> { typedef Dummy* Type; }; 

struct Foo { 
    template<int N> Foo(const char (&)[N]) { BARK; } 
    template<int N> Foo(char (&)[N]) { BARK; } 
    template<typename T> Foo(T, typename IsCharPtr<T>::Type=0) { BARK; } 
}; 

const char a[] = "x"; 
const char* b = "x"; 
const char* f() { return b; } 

int main() { 
    char buffer[10] = "lkj"; 
    char* c = buffer; 
    Foo l("x");  // Foo::Foo(const char (&)[N]) [N = 2] 
    Foo aa(a);  // Foo::Foo(const char (&)[N]) [N = 2] 
    Foo bb(b);  // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = const char *] 
    Foo cc(c);  // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = char *] 
    Foo ee(buffer); // Foo::Foo(char (&)[N]) [N = 10] 
    Foo ff(f()); // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = const char *] 
    return 0; 
} 
+0

मुझे लगता है कि इसे वैध उत्तर के रूप में स्वीकार किया जाना चाहिए। –

+0

ठीक है, मैंने नहीं सोचा था कि यह संभव था! –

+0

क्या होगा यदि एक const_cast एक char buffer है? तब मेरा मानना ​​है कि टेम्पलेट फू (कॉन्स चार (&) [एन]) {बार्क; } इस्तेमाल किया जाएगा। –