2015-10-28 11 views
15

मुझे पता है कि यह बहुत अधिक जावा या सी # लगता है। हालांकि, क्या यह std::to_string फ़ंक्शन के लिए इनपुट के रूप में अपनी खुद की कक्षा को वैध बनाने के लिए संभव/अच्छा/बुद्धिमान है? उदाहरण:उपयोगकर्ता द्वारा परिभाषित वर्ग बनाना std :: to_string (सक्षम)

class my_class{ 
public: 
std::string give_me_a_string_of_you() const{ 
    return "I am " + std::to_string(i); 
} 
int i; 
}; 

void main(){ 
    my_class my_object; 
    std::cout<< std::to_string(my_object); 
} 

अगर ऐसी कोई बात है (और मुझे लगता है कि), यह करने के लिए सबसे अच्छा तरीका क्या है?

+3

संबंधित [क्या कक्षा को स्ट्रिंग में बदलने का एक मानक तरीका है] (http://stackoverflow.com/q/33357480/ 1708801) –

+0

इसके अलावा http://stackoverflow.com/questions/234724/is-it-possible-to-serialize-and-deserialize-a-class-in-c – Lol4t0

+0

@ShafikYaghmour हाँ यह हो सकता है .. हालांकि, समाधान कक्षा को _stringable –

उत्तर

11

सबसे पहले, कुछ ADL की मदद:

namespace notstd { 
    namespace adl_helper { 
    using std::to_string; 

    template<class T> 
    std::string as_string(T&& t) { 
     return to_string(std::forward<T>(t)); 
    } 
    } 
    template<class T> 
    std::string to_string(T&& t) { 
    return adl_helper::as_string(std::forward<T>(t)); 
    } 
} 

notstd::to_string(blah) दायरे में std::to_string साथ to_string(blah) के ADL-देखने करेंगे।

उसके बाद हम आपके वर्ग को संशोधित:

class my_class{ 
public: 
    friend std::string to_string(my_class const& self) const{ 
    return "I am " + notstd::to_string(self.i); 
    } 
    int i; 
}; 

और notstd::to_string(7) करता है के रूप में अब notstd::to_string(my_object) उचित to_string पाता है,।

एक स्पर्श के साथ अधिक काम करने के साथ, हम .tostring() विधियों को ऑटो-डिज़ाइन और उपयोग करने के तरीकों पर भी समर्थन दे सकते हैं।

+0

रिचर्ड के समाधान से यह बेहतर कैसे है? – Slava

+4

@Slava इसे 'to_string' के प्रत्येक उपयोग से पहले मैन्युअल रूप से std :: to_string' का उपयोग करने की आवश्यकता नहीं है; आपको नामस्थान को प्रदूषित करने की आवश्यकता नहीं है। इसके बजाए, प्रदूषण केवल 'notstd :: adl_helper' नामस्थान के भीतर किया जाता है। आप हर बार 'notstd :: to_string' को कॉल करते हैं। मैं 'दोस्त to_string' का उपयोग करता हूं, लेकिन यह एक मुक्त फ़ंक्शन' to_string' के बराबर है। – Yakk

+2

रिचर्ड इस उत्पाद या सेवा को मंजूरी देता है। –

1

आप शायद सिर्फ operator<<() कुछ की तरह ओवरलोड हैं:

std::ostream& operator << (std::ostream& os, const my_class& rhs) { 
    os << "I am " << rhs.i; 
    return os; 
} 
वैकल्पिक रूप से

:

std::ostream& operator << (std::ostream& os, const my_class& rhs) { 
    os << rhs.print_a_string(); 
    return os; 
} 

तो आप बस कर सकते हैं:

int main() { 
    my_class my_object; 
    std::cout << my_object; 

    return 0; 
} 
+0

हाँ यह सच है। लेकिन उदाहरण के लिए मैं यह नहीं कर सकता: 'print_a_string (my_object)' –

+0

तदनुसार संपादित उत्तर, बस 'print_a_string' के व्यवहार को' ऑपरेटर <<() 'अधिभार में डाल दें (और 'print_a_string' से छुटकारा पाएं क्योंकि यह शायद नहीं है अब आवश्यक)। वैकल्पिक रूप से, 'i' के बजाय 'print_a_string()' का उपयोग करें। –

1

आप में अपने स्वयं के to_string निर्धारित कर सकते हैं इसका अपना नामस्थान (उदाहरण के लिए, foo)।

namespace foo { 
    std::string to_string(my_class const &obj) { 
    return obj.string give_me_a_string_of_you(); 
    } 
} 

और के रूप में इसका इस्तेमाल करते हैं: क्योंकि करने के लिए मानक 17.6.4.2.1 नाम स्थान एसटीडी [नाम स्थान acorrding

int main(){ 
    my_class my_object; 
    std::cout<< foo::to_string(my_object); 
} 

Unfortunatelly, आप नाम स्थान std में to_string के अपने स्वयं के संस्करण को परिभाषित नहीं कर सकते हैं। एसटीडी](जोर मेरा):

व्यवहार एक सी ++ प्रोग्राम अनिर्धारित है यदि यह घोषणाओं को जोड़ता है या नेमस्पेस std के भीतर नामस्थान को नामस्थान या परिभाषा के परिभाषाओं को परिभाषित करता है, अन्यथा निर्दिष्ट नहीं किया जाता है। कोई प्रोग्राम किसी भी मानक लाइब्रेरी टेम्पलेट के लिए किसी भी मानक लाइब्रेरी टेम्पलेट के लिए विशेषज्ञता को केवल पर विशेषज्ञता दे सकता है यदि घोषणा उपयोगकर्ता द्वारा परिभाषित प्रकार पर निर्भर करती है और विशेषज्ञता मूल टेम्पलेट के लिए मानक लाइब्रेरी आवश्यकताओं को पूरा करती है और स्पष्ट रूप से प्रतिबंधित नहीं है।

+0

धन्यवाद लेकिन क्या यह दो अलग-अलग नामस्थानों में to_string से अधिक भ्रमित नहीं है? एक ही उपयोग करने का कोई तरीका नहीं है? –

+0

@ हूमहल्फावी नहीं, दुर्भाग्यवश आप अपनी कक्षा के लिए 'std' में' to_string' परिभाषित नहीं कर सकते हैं। – 101010

+0

मुझे लगता है कि उद्धरण का अंतिम भाग (जो बोल्ड में नहीं है) महत्वपूर्ण है। यदि 'std :: to_string()' को टेम्पलेट के रूप में कार्यान्वित किया गया था, तो आप अपने उपयोगकर्ता द्वारा परिभाषित प्रकार के लिए 'std :: to_string()' का विशेषज्ञता बना सकते हैं। हालांकि, इसे एक ओवरलोडेड फ़ंक्शन के रूप में कार्यान्वित किया गया है, इसलिए आप नहीं कर सकते हैं। – Tolli

13

'सर्वश्रेष्ठ' तरीका एक खुला प्रश्न क्या है।

कुछ तरीके हैं।

कहने वाली पहली बात यह है कि कस्टम प्रकार के लिए std::to_string ओवरलोडिंग की अनुमति नहीं है। हम केवल कस्टम प्रकारों के लिए std नेमस्पेस में टेम्पलेट फ़ंक्शंस और कक्षा विशेषज्ञ कर सकते हैं, और std::to_string टेम्पलेट फ़ंक्शन नहीं है।

यह कहा गया है कि to_string का इलाज करने का एक अच्छा तरीका ऑपरेटर या swap का कार्यान्वयन जैसा है। यानी काम करने के लिए तर्क-निर्भर-लुकअप की अनुमति दें।

इसलिए जब हम एक स्ट्रिंग के लिए कुछ परिवर्तित करना चाहते हैं हम लिख सकते हैं:

using std::to_string; 
auto s = to_string(x) + " : " + to_string(i); 

यह सोचते हैं कि एक्स नाम स्थान Y में टाइप एक्स का एक उद्देश्य था और मैं किसी पूर्णांक था, हम तो निर्धारित कर सकते हैं:

namespace Y { 

    std::string to_string(const X& x); 

} 

अब इसका मतलब यह होगा:

लागू to_string(x) वास्तव में Y::to_string(const Y::X&) चयन करता है, और

to_string(i) लागू का चयन करता है std::to_string(int)

आगे जा रहे हैं, यह हो सकता है कि आप ज्यादा ऑपरेटर < < के रूप में भी ऐसा ही करने to_string चाहते हैं, इसलिए तो एक अन्य के मामले में लिखा जा सकता है:

namespace Y { 

    inline std::ostream& operator<<(std::ostream& os, const X& x) { /* implement here */; return os; } 

    inline std::string to_string(const X& x) { 
    std::ostringstream ss; 
    ss << x; 
    return ss.str(); 
    } 
} 
+0

दूसरे के मामले में एक को लिखना प्रदर्शन के मुद्दों में है। – Yakk

+5

अन्य * के संदर्भ में एक * को * * कड़े लूप के भीतर निष्पादित होने पर प्रदर्शन समस्याएं हो सकती हैं। यह पाठक को यह तय करने के लिए छोड़ दिया गया है कि क्या वह दो कोड पथ बनाए रखने के खर्च पर अनुकूलित करना चाहता है या नहीं। –

0

आप std नेम स्पेस में to_string के नए भार के नहीं जोड़ सकते हैं, लेकिन आप अपने नाम स्थान में कर सकते हैं:

namespace my { 
    using std::to_string; 

    std::string to_string(const my_class& o) { 
    return o.give_me_a_string_of_you(); 
    } 
} 

फिर आप सभी प्रकार के लिए my::to_string का उपयोग कर सकते हैं।

int main() 
{ 
    my_class my_object; 

    std::cout << my::to_string(my_object); 
    std::cout << my::to_string(5); 
} 
+1

इस डिज़ाइन का नकारात्मक पक्ष यह है कि आपको अपने सभी 'to_string' को 'नेमस्पेस' में परिभाषित करना होगा, न कि प्रकार के नामस्थान में। – Yakk

0

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

यहां एक फ़ंक्शन टेम्पलेट है जो केवल क्लास प्रकार की वस्तुओं के साथ काम करेगा, और ToString() फ़ंक्शन को कॉल करेगा।

template<typename T, typename = std::enable_if_t<std::is_class<T>::value>> 
    std::string to_string(const T& t) { 
     return t.ToString(); 
    } 

शायद हम इसे std :: स्ट्रिंग के साथ भी काम करना चाहते हैं।

template<> 
    std::string to_string(const std::string& t) { 
     return t; 
    } 

यहां उपयोग के कोड का एक उदाहरण दिया गया है। डमी नेमस्पेस to_s नोट करें। मुझे लगता है कि यदि आप मुख्य फ़ंक्शन में std :: to_string का उपयोग करते हैं, तो यह हमारे टेम्पलेट फ़ंक्शन नाम पर कदम उठाता है, इसलिए हमें इस तरह अप्रत्यक्ष रूप से नाम पेश करना होगा। अगर कोई ऐसा करने का सही तरीका जानता है तो मैं एक टिप्पणी की सराहना करता हूं।

#include <cstring> 
    #include <iostream> 
    #include <string> 
    #include <type_traits> 


    union U { 
     double d; 
     const char* cp; 
    }; 

    struct A { 
     enum State { kString, kDouble }; 
     State state; 
     U u; 

     void Set(const char* cp) { 
     u.cp = cp; 
     state = kString; 
     } 

     std::string ToString() const { 
     switch (state) { 
      case A::kString : return std::string(u.cp); break; 
      case A::kDouble : return std::to_string(u.d); break; 
      default : return "Invalid State"; 
     } 
     } 
    }; 

    namespace to_s { using std::to_string; }; 

    int main() { 
     using namespace to_s; 
     std::string str = "a nice string"; 
     double d = 1.1; 
     A a { A::kDouble, {1.2} }; 

     std::cout << "str: " << to_string(str) << ", d: " << to_string(d) << std::endl; 
     std::cout << "a: " << to_string(a) << std::endl; 
     a.Set(str.c_str()); 
     std::cout << "a: " << to_string(a) << std::endl; 
     std::memset(&a, 'i', sizeof(a)); 
     std::cout << "a: " << to_string(a) << std::endl; 
    } 

यहाँ मैं क्या मिला है:

str: एक अच्छा स्ट्रिंग, डी: 1,100000

एक: 1,200000

एक: एक अच्छा स्ट्रिंग

एक: अमान्य राज्य

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