2011-06-16 16 views
5

मैं एक फ़ंक्शन को अधिभारित करना चाहता हूं ताकि यह किसी भी तरीके से अपना तर्क बदल सके और फिर तर्क का संदर्भ देता है - लेकिन यदि तर्क उत्परिवर्तनीय नहीं है, तो उसे तर्क के प्रति की प्रतिलिपि बनाना चाहिए बजाय। उम्र के साथ इसके साथ गड़बड़ करने के बाद, मैं यहां आया हूं।rvalue function overloading

using namespace std; 

string& foo(string &in) 
{ 
    in.insert(0, "hello "); 
    return in; 
} 

string foo(string &&in) 
{ 
    return move(foo(in)); 
} 

string foo(const string& in) 
{ 
    return foo(string(in)); 
} 

यह कोड सही ढंग से काम करता प्रतीत होता है, लेकिन मुझे यह जानने में दिलचस्पी है कि कोई इसे करने का बेहतर तरीका सोच सकता है या नहीं।

int main(void) 
{ 
    string var = "world"; 
    const string var2 = "const world"; 
    cout << foo(var) << endl; 
    cout << var << endl; 

    cout << foo(var2) << endl; 
    cout << var2 << endl; 

    cout << foo(var + " and " + var2) << endl; 
    return 0; 
} 

सही उत्पादन

hello world 
hello world 
hello const world 
const world 
hello hello world and const world 

है मैं समझ यह थोड़ा neater होगा अगर मैं ऐसा कर सकता है:

यहाँ एक परीक्षण कार्यक्रम है

string& foo(string &in) 
{ 
    in.insert(0, "hello "); 
    return in; 
} 

string foo(string in) 
{ 
    return move(foo(in)); 
} 
बेशक

, यह काम नहीं करता है क्योंकि अधिकांश फ़ंक्शन foo पर संदिग्ध होंगे - 01 में कॉल सहितखुद! लेकिन अगर मैं पहले किसी को प्राथमिकता देने के लिए संकलक को बता सकता हूं ...

जैसा कि मैंने कहा था, मैंने जो कोड पोस्ट किया है वह सही तरीके से काम करता है। मुख्य बात जो मुझे पसंद नहीं है वह दोहराव वाला अतिरिक्त कोड है। अगर मेरे पास इस तरह के कार्यों का एक गुच्छा था तो यह काफी गड़बड़ हो जाएगा, और इसमें से अधिकांश बहुत दोहराएंगे। तो मेरे प्रश्न के दूसरे भाग के रूप में: क्या कोई व्यक्ति दूसरे और तीसरे foo कार्यों के लिए स्वचालित रूप से कोड उत्पन्न करने का तरीका सोच सकता है? उदाहरण के लिए

// implementation of magic_function_overload_generator 
// ??? 

string& foo(string &in); 
magic_function_overload_generator<foo>; 

string& bar(string &in); 
magic_function_overload_generator<bar>; 

// etc 
+7

यह डरावना लगता है। इस समारोह के आधार पर कि मैं फ़ंक्शन को किस प्रकार पास करता हूं, परिणामी स्थिति वापसी मूल्य * और पैरामीटर * पूरी तरह अलग हो सकती है। यह सिर्फ सूक्ष्म बग के लिए पूछ रहा है। क्यों उपयोगकर्ता को यह तय नहीं करना चाहिए कि वह वस्तु को संशोधित करना चाहता है या अलग-अलग कार्यों को स्पष्ट रूप से कॉल करके एक प्रतिलिपि वापस कर सकता है? – jalf

+0

यह मेरे लिए विशेष रूप से डरावना प्रतीत नहीं होता है, लेकिन शायद आप सही हैं। जिस तरह से मैं सोचता हूं वह यह है कि जब भी यह सक्षम हो जाता है तो फ़ंक्शन इनपुट बदलता है; लेकिन अगर यह नहीं कर सकता ... तो यह नहीं करता है, लेकिन यह अभी भी सही वापसी मूल्य देता है। जिस तरह की चीज मैं इसका उपयोग कर सकता हूं वह "पंचट्यूएट" फ़ंक्शन जैसा कुछ है, जो खराब विराम चिह्न वाली स्ट्रिंग लेता है और इसे ठीक करता है। आप सीधे परिणाम को सीट पर भेजना चाहते हैं, या आप बाद में स्ट्रिंग पर कुछ अन्य ऑपरेशन करना चाहेंगे। तो कभी-कभी आप निरंतर मूल्यों को पार कर सकते हैं, और कभी-कभी ... अच्छी तरह से आपको विचार मिलता है। – karadoc

+1

लेकिन मेरा मुद्दा यह है कि क्या या एक या दूसरा होता है कि प्रोग्रामर क्या चाहता है, इस पर निर्भर करता है, लेकिन कुछ अपेक्षाकृत सूक्ष्म अर्थपूर्ण विवरणों पर (तर्क प्रकार का आधार है या नहीं? क्या यह एक रैल्यू है या नहीं?), जो आसानी से निर्णय लेने के बिना प्रोग्राम के बिना समय के साथ बदल सकता है कि "अब मैं ऑब्जेक्ट को संशोधित करने के बजाय एक प्रतिलिपि वापस करना चाहता हूं"। मैं समझता हूं कि आप क्या करने की कोशिश कर रहे हैं, लेकिन यह एक निर्णय है कि प्रोग्रामर आसानी से कर सकता है, और जहां आपकी लाइब्रेरी गलत अनुमान लगाती है, संभावित रूप से बहुत खराब परिणाम हो सकते हैं। – jalf

उत्तर

4

मैं संदर्भ की सभी को एक साथ छुटकारा पाने के हैं और सिर्फ एक समारोह है कि गुजरता है और मूल्य द्वारा रिटर्न लिखें:

std::string foo(std::string in) 
{ 
    in.insert(0, "hello "); 
    return in; 
} 

आप एक lvalue पार कर लेते हैं, इनपुट स्ट्रिंग कॉपी किया जायेगा। यदि आप एक रावल्यू पास करते हैं, तो इसे स्थानांतरित कर दिया जाएगा।

फ़ंक्शन छोड़ते समय, नाम मूल्य अनुकूलन नामित किया जाएगा, इसलिए वापसी मूल रूप से नो-ऑप है। यदि संकलक इसके खिलाफ निर्णय लेता है, तो परिणाम स्थानांतरित हो जाएगा (भले ही in एक अंतराल है)।

रैवल्यू संदर्भों के बारे में अच्छी बात यह है कि आपको कम पर विचार करना होगा कि उपयोगकर्ता कोड में संदर्भ को दक्षता प्राप्त करने के लिए कहां रखा जाए। चलने योग्य प्रकारों के साथ, पास-दर-मूल्य व्यावहारिक रूप से उतना ही कुशल होता है जितना इसे प्राप्त होता है।

+0

यह मूल रूप से मैंने जो किया है वह समाप्त हो गया है। आधा, और दूसरों ने मुझे विश्वास दिलाया है कि मैं पूरी चीज को अतिनिर्धारित कर रहा था। एक समारोह में उत्परिवर्तनीय और अपरिवर्तनीय दोनों संस्करणों को करने की आवश्यकता नहीं होती है - और यह वास्तव में अधिक भ्रमित है यदि एक समारोह उन दोनों चीजों को करता है। मुझे लगता है कि कुछ ठंडा करने के लिए रावल संदर्भों का उपयोग करने के विचार से मुझे अतिरंजित हो गया। मेरी इच्छा है कि मैंने कभी भी रावल्यू संदर्भों के बारे में कभी नहीं सुना होगा, तो मैंने इस समाधान को पहले स्थान पर चुना होगा और आधा दिन बर्बाद नहीं किया होगा! – karadoc

+0

चिंता न करें, अच्छी चीजें अभी भी होती हैं - हुड के नीचे;) – fredoverflow

1

सरल दृष्टिकोण का पालन करने के बारे में क्या?

string& foo (string &change) // this accepts mutable string 
{ 
    change = string("hello ") + change; 
    return change; 
} 

string foo (const string &unchange) // this accepts not mutable string 
{ 
    return string("hello ") + unchange; 
} 

इसे output here देखें।

+0

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

+2

@karadoc: आप उत्परिवर्ती के मामले में आसानी से गैर-उत्परिवर्तनीय लिख सकते हैं। 'int notModifying (const int और i) {int ii = i; वापसी संशोधित (ii); } ' –

0

उसी तरह से @ iammilind के जवाब के रूप में है, लेकिन बिना दोहराव:

#include <iostream> 
using namespace std; 

string foo(const string &unchange) { 
    return string("hello ") + unchange; 
} 

string& foo(string &change) { 
    return change = foo(static_cast<const string&>(foo)); 
} 

int main(int argc, char** argv) { 
    string a = "world"; 
    const string b = "immutable world"; 
    cout << foo(a) << '\n' << foo(b) << '\n'; 
    cout << foo(a) << '\n' << foo(b) << '\n'; 
} 

एनबी: तुम भी यहाँ const_cast इस्तेमाल कर सकते हैं const योग्यता जोड़ने के लिए।

2

पूरा सवाल यह है कि आप ऐसे अधिभार क्यों चाहते हैं? ये सभी अधिभार एक इंटरफ़ेस निर्दिष्ट करते हैं: foo (x)। लेकिन x parameterinput या input/output पैरामीटर के प्रकार के आधार पर हो सकता है। यह बहुत, बहुत त्रुटि-प्रवण है। एक उपयोगकर्ता यह सुनिश्चित करने के लिए कुछ अतिरिक्त काम करेगा कि उसका चर बदल नहीं जाएगा। कभी भी उत्पादन कोड में ऐसा न करें।

मैं ऐसे भार के साथ सहमत होगा: अगर यह एक अस्थायी नहीं है

string foo(string &&in); 
string foo(const string& in); 

इनपुट पैरामीटर कभी नहीं बदला गया है और, एक ही समय में, आप अस्थायी वस्तुओं का पुन: उपयोग। यह काफी उचित लगता है।

लेकिन, आप इतने सारे अधिभार क्यों उत्पन्न करना चाहते हैं? & & अधिभार अनुकूलन के लिए है। मैं बहुत नाजुक अनुकूलन कहूंगा। क्या आप वाकई इसे कई स्थानों पर चाहिए?

वैसे भी, यदि आप वास्तव में सी ++ कोड उत्पन्न करना चाहते हैं, तो टेम्पलेट वास्तव में अच्छी पसंद नहीं हैं। मैं इसके लिए कुछ बाहरी उपकरण का उपयोग करूंगा। व्यक्तिगत रूप से, मैं Cog पसंद करता हूं।

0

यदि आप दक्षता के बारे में चिंतित नहीं हैं, तो आप मूल्य से गुजर सकते हैं या कॉन्स्ट संदर्भ द्वारा पास कर सकते हैं और एक प्रतिलिपि बना सकते हैं और इसके साथ किया जा सकता है।

हालांकि, यदि आप दक्षता के बारे में चिंतित हैं, तो मुझे नहीं लगता कि इस reply में मूल्य सुझाव द्वारा पास का सबसे अच्छा तरीका है। ऐसा इसलिए है क्योंकि मुझे लगता है कि यह अतिरिक्त प्रतियों/चालों में परिणाम देता है, क्योंकि एनआरवीओ केवल स्थानीय चर के साथ काम करता है, पैरामीटर नहीं। मुझे लगता है कि जिस तरह से है कि C++ 0x में चलता रहता है/प्रतियां से बचा जाता है दोहरी भार के रूप में निम्नलिखित कोड द्वारा सचित्र है,:

#include <iostream> 

struct A 
{ 
    A() : i(0) {} 
    A(const A& x) : i(x.i) { std::cout << "Copy" << std::endl; } 
    A(A&& x) : i(x.i) { std::cout << "Move" << std::endl; } 
    void inc() { ++i; } 
    int i; 
}; 

A f1(const A& x2) { A x = x2; x.inc(); return x; } 
A&& f1(A&& x) { x.inc(); return std::move(x); } 

A f2(A x) { x.inc(); return std::move(x); } 

int main() 
{ 
    A x; 
    std::cout << "A a1 = f1(x);" << std::endl; 
    A a1 = f1(x); 
    std::cout << "A a2 = f1(A());" << std::endl; 
    A a2 = f1(A()); 
    std::cout << "A b1 = f2(x);" << std::endl; 
    A b1 = f2(x); 
    std::cout << "A b2 = f2(A());" << std::endl; 
    A b2 = f2(A()); 
    std::cout << std::endl; 
    std::cout << "A a3 = f1(f1(x));" << std::endl; 
    A a3 = f1(f1(x)); 
    std::cout << "A a4 = f1(f1(A()));" << std::endl; 
    A a4 = f1(f1(A())); 
    std::cout << "A b3 = f2(f2(x));" << std::endl; 
    A b3 = f2(f2(x)); 
    std::cout << "A b4 = f2(f2(A()));" << std::endl; 
    A b4 = f2(f2(A())); 
    std::cout << std::endl; 
    std::cout << "A a5 = f1(f1(f1(x)));" << std::endl; 
    A a5 = f1(f1(f1(x))); 
    std::cout << "A a6 = f1(f1(f1(A())));" << std::endl; 
    A a6 = f1(f1(f1(A()))); 
    std::cout << "A b5 = f2(f2(f2(x)));" << std::endl; 
    A b5 = f2(f2(f2(x))); 
    std::cout << "A b6 = f2(f2(f2(A())));" << std::endl; 
    A b6 = f2(f2(f2(A()))); 
} 

निम्नलिखित में से कौन परिणाम का उत्पादन:

A a1 = f1(x); 
Copy 
A a2 = f1(A()); 
Move 
A b1 = f2(x); 
Copy 
Move 
A b2 = f2(A()); 
Move 

A a3 = f1(f1(x)); 
Copy 
Move 
A a4 = f1(f1(A())); 
Move 
A b3 = f2(f2(x)); 
Copy 
Move 
Move 
A b4 = f2(f2(A())); 
Move 
Move 

A a5 = f1(f1(f1(x))); 
Copy 
Move 
A a6 = f1(f1(f1(A()))); 
Move 
A b5 = f2(f2(f2(x))); 
Copy 
Move 
Move 
Move 
A b6 = f2(f2(f2(A()))); 
Move 
Move 
Move 

आप सक्षम हो सकता है कई भार के लेखन, उदाहरण के लिए से बचने के लिए कुछ टेम्पलेट चालें करने के लिए:

template <class T> 
param_return_type<T&&>::type f3(T&& y, typename std::enable_if<...>::type* dummy = 0) 
{ 
    typedef return_t param_return_type<T&&>::type; 
    return_t x = static_cast<return_t>(y); 
    x.inc(); 
    return static_cast<return_t>(x); 
} 

कहाँ param_return_type<T>::typeT है जब (const) T& पारित कर दिया, और T&& जब T&& पारित किया गया। std::enable_if<...> यदि आप केवल इस टेम्पलेट को विशेष पैरामीटर लेने के लिए चाहते हैं तो आप इसका उपयोग कर सकते हैं।

मुझे यकीन नहीं था कि param_return_type<T>::type की परिभाषा कैसे लिखें, क्योंकि ऐसा लगता है कि std::remove_lvalue_reference नहीं है। अगर कोई जानता है कि कैसे, मेरी पोस्ट में संपादित/जोड़ने के लिए स्वतंत्र महसूस करें।