झूलने वाले कॉन्स्टेंस रेफ को कैसे हल करें


18

निम्नलिखित लघु कार्यक्रम

#include <vector>
#include <iostream>

std::vector<int> someNums()
{
    return {3, 5, 7, 11};
}

class Woop
{
public:
    Woop(const std::vector<int>& nums) : numbers(nums) {}
    void report()
    {
        for (int i : numbers)
            std::cout << i << ' ';
        std::cout << '\n';
    }
private:
    const std::vector<int>& numbers;
};

int main()
{
    Woop woop(someNums());
    woop.report();
}

एक ख़तरनाक संदर्भ समस्या है, जिसके बारे में चेतावनी देने के लिए कोई संकलक नहीं लगता है। मुद्दा यह है कि टेम्पोररी कांस्ट-रेफ़ के लिए बाध्य हो सकते हैं, जिसे आप तब रख सकते हैं। सवाल तो यह है; क्या इस समस्या से बचने का एक तरीका है? अधिमानतः एक जिसमें नित्य शुद्धता का त्याग शामिल नहीं है, या हमेशा बड़ी वस्तुओं की प्रतियां बनाना शामिल है।


4
यह मुश्किल है। मैं आपको यह सुनिश्चित कर सकता हूं कि मैं एक सदस्य चर कांस्ट रेफरेंस बनाने से पहले दो बार सोचता हूं। यदि संदेह है, तो मैं इस डेटा को किसी तरह से मॉडल करने पर विचार करूंगा कि स्मार्ट पॉइंटर शामिल हो सकता है (या तो std::unique_ptrअनन्य स्वामित्व के लिए, std::shared_ptrया साझा स्वामित्व, या std::weak_ptr, कम से कम, खोए हुए डेटा को पहचानें)।
शेफ

C ++ में, आपको उस चीज के लिए भुगतान नहीं करना चाहिए जिसकी आपको आवश्यकता / उपयोग नहीं है। यह प्रोग्रामर पर निर्भर है कि वह संदर्भित ऑब्जेक्ट का जीवनकाल समाप्त नहीं होता है जबकि संदर्भ अभी भी उपयोग में है / मौजूद है। कच्चे पॉइंटर्स के लिए एक ही बात, ... आपके द्वारा मांगी गई सुविधाओं को लाने के लिए स्मार्ट पॉइंटर्स हैं :)
Fareanor

2
संदर्भ सदस्यों को हमेशा एक गलती होती है: जड़ी बूटी की दुकान.com
मैक्सिम

हालांकि संकलक चेतावनी नहीं देता है, यह बग वेलग्रिंड द्वारा कैटलेबल है और -fsanitize=address। मुझे नहीं लगता कि प्रदर्शन का त्याग किए बिना इससे बचने के लिए कोई सर्वोत्तम अभ्यास है।
ks1322

जवाबों:


8

ऐसी स्थिति में जब कोई विधि वापस लौटने के बाद संदर्भ रखती std::reference_wrapperहै तो सामान्य संदर्भ के बजाय उसका उपयोग करना अच्छा होता है :

#include <functional>

class Woop
{
public:
    using NumsRef = ::std::reference_wrapper<const std::vector<int>>;
    Woop(NumsRef nums) : numbers_ref{nums} {}
    void report()
    {
        for (int i : numbers_ref.get())
            std::cout << i << ' ';
        std::cout << '\n';
    }
private:
    NumsRef numbers_ref;
};
  1. यह पहले से ही ओवरलोड के एक सेट के साथ आता है, जो कि प्रतिद्वंद्वियों के बंधन को रोकता है और अस्थायी रूप से अनजाने में गुजरता है, इसलिए Woop (std::vector<int> const &&) = delete;आपकी विधि के लिए एक अतिरिक्त निषिद्ध अधिभार लेने से परेशान होने की आवश्यकता नहीं है :
Woop woop{someNums()}; // error
woop.report();
  1. यह अंतराल के बाध्यकारी बंधन की अनुमति देता है, इसलिए यह मौजूदा मान्य चालन को नहीं तोड़ेगा:
auto nums{someNums()};
Woop woop{nums}; // ok
woop.report();
  1. यह अंतराल के स्पष्ट बंधन की अनुमति देता है जो यह इंगित करने के लिए एक अच्छा अभ्यास है कि कॉलर लौटने के बाद संदर्भ रखेगा:
auto nums{someNums()};
Woop woop{::std::ref(nums)}; // even better because explicit
woop.report();

10

अपनी कक्षा को कमज़ोर बनाने का एक तरीका यह हो सकता है कि एक डिलीट किए गए कंस्ट्रक्टर को जोड़ा जाए जो राइट-रेफ लेता है। यह आपके वर्ग उदाहरण को अस्थायी लोगों के लिए बाइंडिंग बनाने से रोक देगा।

Woop(std::vector<int>&& nums)  =delete;

यह हटाए गए निर्माणकर्ता वास्तव में ओ / पी कोड को संकलित नहीं करेंगे, जो आपके लिए देख रहे व्यवहार हो सकता है?


3

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

हालांकि, अगर ऐसा है, तो मुझे लगता है कि अन्य वर्तमान में पोस्ट किए गए उत्तर बिंदु के बगल में हैं। वे सभी आपको दिखा रहे हैं कि Woopउन मूल्यों को कैसे बनाया जाए ।

यदि आप आश्वस्त कर सकते हैं कि आप जिस वेक्टर से गुजरते हैं, वह आपके Woopउदाहरण को रेखांकित करेगा , तो आप स्पष्ट रूप Woopसे एक प्रतिद्वंद्विता से निर्माण को अक्षम कर सकते हैं । इस C ++ 11 सिंटैक्स का उपयोग करना संभव है:

Woop (std::vector<int> const &&) = delete;

अब आपका उदाहरण कोड अब संकलित नहीं होगा। संकलक एक त्रुटि के समान देता है:

prog.cc: In function 'int main()':
prog.cc:29:25: error: use of deleted function 'Woop::Woop(const std::vector<int>&&)'
   29 |     Woop woop(someNums());
      |                         ^
prog.cc:15:5: note: declared here
   15 |     Woop(std::vector<int> const &&) = delete;
      |     ^~~~

पुनश्च: आप शायद एक स्पष्ट रचनाकार चाहते हैं, उदाहरण के लिए देखें कि स्पष्ट खोजशब्द का क्या अर्थ है?


मुझे लगता है कि वहां आपका जवाब चुरा लिया गया है। माफ़ करना!
जेम टेलर

1

उस विशेष मामले को रोकने के लिए, आप या तो एक पॉइंटर लेने का विकल्प चुन सकते हैं (क्योंकि Weep(&std::vector<int>{1,2,3})अनुमति नहीं है) या आप एक गैर-कॉन्स्टेंस संदर्भ ले सकते हैं जो अस्थायी पर भी त्रुटि करेगा।

Woop(const std::vector<int> *nums);
Woop(std::vector<int> *nums);
Woop(std::vector<int>& nums);

ये अभी भी गारंटी नहीं देते हैं कि मूल्य वैध रहता है, लेकिन कम से कम सबसे आसान गलती को रोकता है, एक प्रतिलिपि नहीं बनाता है, और numsएक विशेष तरीके (जैसे std::shared_ptrया std::weak_ptrकरता है) में निर्मित होने की आवश्यकता नहीं है ।

std::scoped_lockम्यूटेक्स के लिए एक संदर्भ लेना एक उदाहरण होगा, और जहां अद्वितीय / साझा / कमजोर ptr वास्तव में नहीं चाहता है। अक्सर std::mutexयह केवल एक मूल सदस्य या स्थानीय चर होगा। आपको अभी भी बहुत सावधान रहना होगा, लेकिन इन मामलों में आमतौर पर जीवन अवधि निर्धारित करना आसान होता है।

std::weak_ptrगैर-स्वामित्व के लिए एक और विकल्प है, लेकिन फिर आप फोन करने वाले को उपयोग करने के लिए मजबूर करते हैं shared_ptr(और इस तरह से भी आवंटित करते हैं), और कभी-कभी वह नहीं चाहता था।

यदि कोई प्रतिलिपि ठीक है, तो वह समस्या से बच जाती है।

यदि Woopस्वामित्व लेना चाहिए या तो एक आर-मान के रूप में पास करें और (और पूरी तरह से पॉइंटर / संदर्भ मुद्दों से बचें), या उपयोग करें unique_ptrयदि आप स्वयं मूल्य को स्थानांतरित नहीं कर सकते हैं या पॉइंटर को वैध बने रहना चाहते हैं।

// the caller can't continue to use nums, they could however get `numbers` from Woop or such like
// or just let Woop only manipulate numbers directly.
Woop(std::vector<int> &&nums) 
   : numbers(std::move(nums)) {}
std::vector<int> numbers;

// while the caller looses the unique_ptr, they might still use a raw pointer, but be careful.
// Or again access numbers only via Woop as with the move construct above.
Woop(std::unique_ptr<std::vector<int>> &&nums) 
    : numbers(std::move(nums)) {}
std::unique_ptr<std::vector<int>> numbers;

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


1

आप उपयोग कर सकते हैं template programmingऔर arraysयदि आप एक वस्तु रखना चाहते हैं जो एक constकंटेनर रखती है । constexprकंस्ट्रक्टर के कारण और constexpr arraysआप प्राप्त करते हैं const correctnessऔर compile time execution

यहाँ एक पोस्ट है जो दिलचस्प हो सकती है: std :: एक const वेक्टर ले जाएँ

#include <array>
#include <iostream>
#include <vector>


std::array<int,4>  someNums()
{
    return {3, 5, 7, 11};
}


template<typename U, std::size_t size>
class Woop
{
public:

template<typename ...T>
    constexpr Woop(T&&... nums) : numbers{nums...} {};

    template<typename T, std::size_t arr_size>
    constexpr Woop(std::array<T, arr_size>&& arr_nums) : numbers(arr_nums) {};

    void report()
    const {
        for (auto&& i : numbers)
            std::cout << i << ' ';
         std::cout << '\n';
    }



private: 
    const std::array<U, size> numbers;
    //constexpr vector with C++20
};

int main()
{
    Woop<int, 4> wooping1(someNums());
    Woop<int, 7> wooping2{1, 2, 3, 5, 12 ,3 ,51};

    wooping1.report();
    wooping2.report();
    return 0;
}

कोड चलाएं

आउटपुट:

3 5 7 11                                                                                                                        
1 2 3 5 12 3 51

1
संख्याओं के साथ std::arrayयह नकल करने की गारंटी है, भले ही एक चाल अन्यथा उपलब्ध हो। उसके ऊपर wooping1और wooping2समान प्रकार नहीं हैं, जो आदर्श से कम है।
sp2danny

@ sp2danny आपकी प्रतिक्रिया के लिए धन्यवाद और मुझे दोनों बिंदुओं पर आपसे सहमत होना है। user7860670 ने एक बेहतर समाधान प्रदान किया :)
M.Mac
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.