2012-01-03 12 views
42

मेरी सी ++ आवेदन में (दृश्य स्टूडियो 2010 का उपयोग), मैं इस तरह, एक std :: समारोह स्टोर करने के लिए की जरूरत है:क्या मुझे एक std :: फ़ंक्शन कॉपी करना चाहिए या क्या मैं हमेशा इसका संदर्भ ले सकता हूं?

class MyClass 
    { 
    public: 
     typedef std::function<int(int)> MyFunction; 
     MyClass (Myfunction &myFunction); 
    private: 
     MyFunction m_myFunction; // Should I use this one? 
     MyFunction &m_myFunction; // Or should I use this one? 
    }; 

आप देख सकते हैं, मैं निर्माता में एक संदर्भ के रूप में कार्य तर्क गयी।

लेकिन, मेरी कक्षा में फ़ंक्शन को स्टोर करने का सबसे अच्छा तरीका क्या है?

  • क्या मैं फ़ंक्शन को संदर्भ के रूप में संग्रहीत कर सकता हूं क्योंकि std :: function केवल एक फ़ंक्शन-पॉइंटर है और फ़ंक्शन का 'निष्पादन योग्य कोड' स्मृति में रहने की गारंटी है?
  • क्या मुझे लैम्ब्डा पास होने के मामले में एक प्रतिलिपि बनाना है और कॉलर लौटाता है?

मेरा आंत महसूस करता है कि संदर्भ (यहां तक ​​कि एक कॉन्स्ट-रेफरेंस) स्टोर करना सुरक्षित है। मैं संकलक समय पर लैम्ब्डा के लिए कोड उत्पन्न करने की अपेक्षा करता हूं, और एप्लिकेशन को चलाने के दौरान इस निष्पादन योग्य कोड को 'वर्चुअल' मेमोरी में रखें। इसलिए निष्पादन योग्य कोड कभी भी 'हटाया नहीं जाता' है और मैं इसे सुरक्षित रूप से संदर्भित कर सकता हूं। लेकिन क्या यह वास्तव में सच है?

+3

इससे कोई फर्क नहीं पड़ता कि 'std :: function' में क्या शामिल है ... यदि आप किसी को संदर्भित करते हैं जिसे संदर्भित किया जा रहा है, तो आपको कोई समस्या है। – Xeo

+0

@Alex लपेटा गया फ़ंक्शन ऑब्जेक्ट, लैम्ब्डा या अन्य प्रकार का कोई फर्क नहीं पड़ता। सटीक वही सिद्धांत यहां किसी भी वस्तु के लिए लागू होते हैं जिसके लिए एक संदर्भ (सूचक सहित) का गठन किया जा सकता है। तो, अगर आप बाद में कॉल करने का प्रयास करते हैं तो डरावनी गारंटी दी जाती है (ए) एक 'std :: function'' std :: context_wrapper' द्वारा एक मज़ेदार को पारित किया जाता है, जहां निर्दिष्ट मज़ेदार का जीवनकाल समाप्त हो गया है, और/या (बी) एक संदर्भ 'std :: function' जहां रेफरेंस का जीवनकाल समाप्त हो गया है। –

उत्तर

40

क्या मैं फ़ंक्शन को संदर्भ के रूप में संग्रहीत कर सकता हूं क्योंकि std :: function केवल एक फ़ंक्शन-पॉइंटर है और फ़ंक्शन का 'निष्पादन योग्य कोड' स्मृति में रहने की गारंटी है?

std::function बहुत ही फ़ंक्शन पॉइंटर नहीं है। यह एक मनमानी कॉल करने योग्य ऑब्जेक्ट के चारों ओर एक रैपर है, और उस ऑब्जेक्ट को स्टोर करने के लिए उपयोग की जाने वाली मेमोरी का प्रबंधन करता है। किसी भी अन्य प्रकार के साथ, संदर्भ को केवल पर संग्रहीत करना सुरक्षित है, आपके पास यह गारंटी देने का कोई अन्य तरीका है कि जब भी संदर्भ का उपयोग किया जाता है तो निर्दिष्ट ऑब्जेक्ट अभी भी मान्य है।

जब तक आपके पास कोई संदर्भ संग्रहीत करने का कोई अच्छा कारण न हो, और यह गारंटी देने का एक तरीका है कि यह वैध रहता है, तो इसे मूल्य से संग्रहित करें।

const द्वारा निर्मित कन्स्ट्रक्टर के संदर्भ में सुरक्षित है, और शायद मूल्य को पार करने से अधिक कुशल है। गैर-const संदर्भ से गुजरना एक बुरा विचार है, क्योंकि यह आपको अस्थायी रूप से गुजरने से रोकता है, इसलिए उपयोगकर्ता bind, या std::function<int(int)> को छोड़कर किसी भी अन्य कॉल करने योग्य ऑब्जेक्ट का परिणाम सीधे लैम्बडा नहीं दे सकता है।

+0

। मुझे बस एहसास है (आपके उत्तर के लिए धन्यवाद) कि std :: function फ़ंक्शन-पॉइंटर के चारों ओर एक रैपर है, और यहां तक ​​कि यदि फ़ंक्शन या लैम्ब्डा स्मृति में रहता है, तो इसके चारों ओर रैपर होना आवश्यक नहीं है। संभावित रूप से (शायद?) संकलक मेरे कन्स्ट्रक्टर को लैम्ब्डा पास करते समय फ्लाई पर std :: फ़ंक्शन उत्पन्न करता है, जिसका अर्थ है कि यह वास्तव में कॉल के बाद चला गया है। धन्यवाद। – Patrick

+4

मूल्य के आधार पर निर्माता को पास करें, फिर std :: सदस्य में स्थानांतरित करें। http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ –

+4

"कुछ काम करने से कोई काम बेहतर नहीं है - मूल 2013 जा रहा है"। ऑब्जेक्ट (1 कार्य) बनाएं, कॉन्स रेफ (कोई काम नहीं) द्वारा पास करें, (1 कार्य) का उपयोग करें। ऑब्जेक्ट (1 कार्य) बनाएं, मान और स्थानांतरित करें (1 कार्य), उपयोग करें (1 कार्य)। तो इस मामले में कॉन्स रेफरी से गुजरना बेहतर है। – Jagannath

2

जितनी चाहें उतनी कॉपी करें। यह कॉपी करने योग्य है। मानक पुस्तकालय में अधिकांश एल्गोरिदम की आवश्यकता होती है कि मज़ेदार हैं।

हालांकि, संदर्भ से गुजरना संभवतः गैर-मामूली मामलों में तेज़ होगा, इसलिए मैं निरंतर संदर्भ और मूल्य से भंडारण करने का सुझाव देता हूं ताकि आपको जीवन चक्र प्रबंधन की परवाह न हो। तो:

class MyClass 
{ 
public: 
    typedef std::function<int(int)> MyFunction; 
    MyClass (const Myfunction &myFunction); 
      // ^^^^^ pass by CONSTANT reference. 
private: 
    MyFunction m_myFunction; // Always store by value 
}; 

निरंतर या rvalue संदर्भ से गुज़र कर आप फोन करने वाले है कि आप समारोह में बदलाव नहीं करेगी, जबकि आप अभी भी इसे कॉल कर सकते हैं वादा करता हूँ। यह आपको गलती से फ़ंक्शन को संशोधित करने से रोकता है और इसे जानबूझकर से बचा जाना चाहिए, क्योंकि यह वापसी मूल्य का उपयोग करने से कम पठनीय है।

संपादित करें: मैंने मूल रूप से ऊपर "कॉन्स्टेंट या रावल्यू" कहा था, लेकिन डेव की टिप्पणी ने मुझे इसे देखा और वास्तव में रैवल्यू संदर्भ लालसा स्वीकार नहीं करता है।

+2

उहह ... रावल्यू संदर्भ द्वारा गुजरने से उपयोगकर्ता को इसे 'स्थानांतरित' करने के लिए मजबूर किया जाता है - निश्चित रूप से "वादा नहीं है कि आप फ़ंक्शन को संशोधित नहीं करेंगे"। साथ ही, अधिक आधुनिक अभ्यास मूल्य से स्वीकार करना है यदि आप इसे स्टोर करने जा रहे हैं (और 'std :: move' इसे अपने संग्रहीत सदस्य में ले जाएं), या यदि आप इसे स्टोर नहीं करेंगे तो निरंतर संदर्भ से गुज़रें। – David

+0

@ डेव: आप सही हैं। तो यह अभी भी बट में दर्द है यदि आपको अपने आक्रमणकारी में विधियों की तरह एल-या-रावल्यू लेने के लिए एक फ़ंक्शन की आवश्यकता होती है। छी। वास्तव में –

2

मैं तुम्हें एक प्रतिलिपि बनाने के लिए सुझाव है:

MyFunction m_myFunction; //prefferd and safe! 

यह सुरक्षित है, क्योंकि मूल वस्तु ही destructing क्षेत्र से बाहर जाता है, प्रति अब भी वर्ग उदाहरण में उपलब्ध नहीं होगा।

7

यदि आप संदर्भ द्वारा कन्स्ट्रक्टर में फ़ंक्शन पास करते हैं, और इसकी प्रतिलिपि नहीं बनाते हैं, तो जब आप इस ऑब्जेक्ट के बाहर कार्यक्षेत्र से बाहर निकलते हैं तो आप भाग्य से बाहर होंगे, क्योंकि संदर्भ नहीं होगा अब मान्य हो। पिछले जवाब में पहले से ही यही कहा गया है।

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

बेशक, हालांकि आप चीजें करते हैं, तो आप चर में फ़ंक्शन में असाइन करते समय संभावित रूप से एक और प्रतिलिपि बनाते हैं, इसलिए उस प्रति को समाप्त करने के लिए std::move का उपयोग करें। उदाहरण:

class MyClass 
{ 
public: 
    typedef std::function<int(int)> MyFunction; 

    MyClass (Myfunction myFunction): m_myfunction(std::move(myFunction)) 
     {} 

private: 
    MyFunction m_myFunction; 
}; 

तो, अगर वे ऊपर के लिए एक rvalue में गुजरती हैं, संकलक दूर का अनुकूलन निर्माता में पहली कॉपी करना और std :: कदम एक दूसरे :)

को हटा यदि आपका (केवल) निर्माता एक स्थिरांक संदर्भ लेता है, आप होगा कि यह कैसे में पारित कर दिया है की परवाह किए बिना समारोह में यह की एक प्रतिलिपि बनाने की जरूरत

विकल्प अलग lvalues ​​और rvalues ​​से निपटने के लिए दो कंस्ट्रक्टर्स परिभाषित करने के लिए, यह है:।

class MyClass 
{ 
public: 
    typedef std::function<int(int)> MyFunction; 

    //takes lvalue and copy constructs to local var: 
    MyClass (const Myfunction & myFunction): m_myfunction(myFunction) 
     {} 
    //takes rvalue and move constructs local var: 
    MyClass (MyFunction && myFunction): m_myFunction(std::move(myFunction)) 
     {} 

private: 
    MyFunction m_myFunction; 
}; 

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

(शायद देखा एक निष्पक्ष बिट यहाँ के आसपास) प्रासंगिक संदर्भ (और एक बहुत अच्छा पढ़ा): http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

3

एक सामान्य नियम (विशेष रूप से अगर आप कुछ अत्यधिक पिरोया प्रणाली के लिए इन का उपयोग कर रहे हैं), पास के रूप में मूल्य से वास्तव में किसी थ्रेड के भीतर से सत्यापित करने का कोई तरीका नहीं है कि अंतर्निहित वस्तु अभी भी संदर्भ प्रकार के साथ है, इसलिए आप स्वयं को बहुत ही खराब दौड़ और डेडलॉक बग तक खोलते हैं।

एक और विचार std :: फ़ंक्शन में कोई छुपा राज्य चर है, जिसके लिए संशोधन थ्रेड-सुरक्षित होने की संभावना नहीं है। इसका मतलब यह है कि अगर अंतर्निहित फ़ंक्शन कॉल थ्रेड-सुरक्षित है, तो std :: फ़ंक्शन रैपर का "()" इसके आसपास कॉल नहीं हो सकता है। आप std :: फ़ंक्शन की थ्रेड-स्थानीय प्रतियों का उपयोग करके वांछित व्यवहार को पुनर्प्राप्त कर सकते हैं क्योंकि उनमें से प्रत्येक के पास राज्य चर की पृथक प्रति होगी।

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