वेक्टर बढ़ने पर शब्दार्थ को कैसे लागू करें?


93

मेरे पास एक std::vectorनिश्चित वर्ग की वस्तुएं हैं A। वर्ग गैर-तुच्छ है और इसमें कॉपी बिल्डरों और स्थानांतरित कंस्ट्रक्टरों को परिभाषित किया गया है।

std::vector<A>  myvec;

यदि मैं वेक्टर को Aऑब्जेक्ट्स (उदाहरण के लिए myvec.push_back(a)) का उपयोग करके भरता हूं , तो वेक्टर आकार में बढ़ेगा, A( const A&)वेक्टर में तत्वों की नई प्रतियों को तुरंत कॉपी करने के लिए कॉपी कंस्ट्रक्टर का उपयोग करेगा ।

क्या मैं किसी तरह यह लागू कर सकता हूं कि क्लास Aका मूव कंस्ट्रक्टर इसके बजाय इस्तेमाल कर रहा है ?


5
आप एक चाल-जागरूक वेक्टर कार्यान्वयन का उपयोग करके कर सकते हैं।
के-बैलो

2
क्या आप इसे प्राप्त करने के लिए थोड़ा और विशिष्ट हो सकते हैं?
बर्टविम वैन बीस्ट

1
आप बस एक चाल-जागरूक वेक्टर कार्यान्वयन का उपयोग करते हैं। यह आपके मानक पुस्तकालय कार्यान्वयन जैसा लगता है (जो यह btw है?) चाल-पता नहीं है। आप बूस्ट से मूव-अवेयर कंटेनर के साथ कोशिश कर सकते हैं।
K-ballo

1
खैर, मैं gcc 4.5.1 का उपयोग करता हूं, जो कि जागरूक है।
बर्टविम वैन बीस्ट

मेरे कोड में यह कॉपी कंस्ट्रक्टर को निजी बनाने के लिए काम करता था, भले ही मूव कंस्ट्रक्टर के पास स्पष्ट "noexcept" नहीं था।
Arne

जवाबों:


129

आपको C ++ (विशेष रूप से std::vector) को सूचित करने की आवश्यकता है कि आपके मूव कंस्ट्रक्टर और डिस्ट्रॉक्टर उपयोग नहीं कर रहे हैं noexcept। फिर वेक्टर के बढ़ने पर मूव कंस्ट्रक्टर को बुलाया जाएगा।

यह एक चाल अवरोधक को घोषित करने और कार्यान्वित करने का तरीका है, जिसका सम्मान किया जाता है std::vector:

A(A && rhs) noexcept { 
  std::cout << "i am the move constr" <<std::endl;
  ... some code doing the move ...  
  m_value=std::move(rhs.m_value) ; // etc...
}

यदि कंस्ट्रक्टर नहीं है noexcept, std::vectorतो इसका उपयोग नहीं कर सकते हैं, तब से यह मानक द्वारा मांग की गई अपवाद की गारंटी को सुनिश्चित नहीं कर सकता है।

मानक में क्या कहा गया है, इसके बारे में अधिक जानने के लिए, C ++ मूवमेंट शब्दार्थ और अपवाद पढ़ें

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

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

A(A && rhs) = default;

ऐसा करने पर, आपको जब संभव हो तो noexcept मिलेगा: क्या डिफ़ॉल्ट मूव कंस्ट्रक्टर को noexcept के रूप में परिभाषित किया गया है?

ध्यान दें कि विज़ुअल स्टूडियो 2015 के शुरुआती संस्करणों और पुराने ने इसका समर्थन नहीं किया, भले ही यह कदम शब्दार्थ का समर्थन करता हो।


ब्याज में से, कैसे करता है impl "पता" है कि क्या value_typeकी चाल ctor है noexcept? शायद भाषा फ़ंक्शन कॉल उम्मीदवार सेट को प्रतिबंधित करती है जब कॉलिंग स्कोप भी एक noexceptफ़ंक्शन है?
ऑर्बिट

1
@LightnessRacesinOrbit मुझे लगता है कि यह कुछ ऐसा कर रहा है जैसे कि en.cppreference.com/w/cpp/types/is_move_constructible । केवल एक चाल का निर्माण हो सकता है इसलिए इसे घोषणा द्वारा स्पष्ट रूप से परिभाषित किया जाना चाहिए।
जोहान लुंडबर्ग

@LightnessRacesinOrbit, मैंने तब से सीखा है कि वास्तव में यह जानने के लिए कोई मानक (उपयोगी / उपयोगी) तरीका नहीं है कि क्या कोई noexceptमूव कंस्ट्रक्टर है। कॉपी कंस्ट्रक्टर is_nothrow_move_constructibleहोने पर सच होगा nothrow। मुझे महंगी nothrowकॉपी कंस्ट्रक्टरों के किसी भी वास्तविक मामले की जानकारी नहीं है, इसलिए यह स्पष्ट नहीं है कि यह वास्तव में मायने रखता है।
जोहान लुंडबर्ग

मेरे लिए काम नहीं करता है। मेरे विध्वंसक, मूव कंस्ट्रक्टर, और मूवमेंट फ़ंक्शंस सभी noexceptहेडर और कार्यान्वयन दोनों में चिह्नित हैं , और जब मैं एक push_back (std:; Move) करता हूं, तब भी यह कॉपी कंस्ट्रक्टर को कॉल करता है। मैं यहां अपने बाल फाड़ रहा हूं।
एलिस्टेयर

1
@ जोहान मैं समस्या पाया। मैं std::move()गलत push_back()कॉल पर उपयोग कर रहा था । उन समयों में से एक जब आप किसी समस्या के लिए इतनी मेहनत कर रहे होते हैं कि आपके सामने स्पष्ट त्रुटि दिखाई नहीं देती है। और फिर यह दोपहर का भोजन था और मैं अपनी टिप्पणी को हटाना भूल गया।
एलेस्टेयर

17

दिलचस्प है, gcc 4.7.2 का वेक्टर केवल मूव कंस्ट्रक्टर का उपयोग करता है यदि दोनों मूव कंस्ट्रक्टर और डिस्ट्रक्टर दोनों हैं noexcept। एक सरल उदाहरण:

struct foo {
    foo() {}
    foo( const foo & ) noexcept { std::cout << "copy\n"; }
    foo( foo && ) noexcept { std::cout << "move\n"; }
    ~foo() noexcept {}
};

int main() {
    std::vector< foo > v;
    for ( int i = 0; i < 3; ++i ) v.emplace_back();
}

यह अपेक्षित परिणाम देता है:

move
move
move

हालांकि, जब मैं हटाने noexceptसे ~foo(), परिणाम अलग है:

copy
copy
copy

मुझे लगता है कि यह भी इस सवाल का जवाब देता है ।


मुझे ऐसा लगता है कि अन्य उत्तर केवल मूव कंस्ट्रक्टर के बारे में बात करते हैं, न कि विध्वंसक होने के बारे में जो अस्पष्ट हैं।
निकोला बेन्स

ठीक है, यह होना चाहिए, लेकिन जैसा कि यह निकलता है, जीसीसी 4.7.2 में यह नहीं था। तो यह समस्या, वास्तव में, जीसीसी के लिए विशिष्ट थी। हालांकि इसे gcc 4.8.0 में तय किया जाना चाहिए। संबंधित स्टैकओवरफ़्लो प्रश्न देखें ।
निकोला बेन्स

-1

ऐसा लगता है, कि एक ही रास्ता (सी ++ 17 और शुरुआती के लिए), को लागू करने के std::vectorलिए reallocation पर चाल शब्दार्थों को कॉपी कंस्ट्रक्टर हटा रहा है :)। इस तरह यह आपके मूव कन्स्ट्रक्टर्स का उपयोग करेगा या कम्पाइल टाइम पर कोशिश करके मर जाएगा :)।

ऐसे कई नियम हैं जहां std::vectorMUST को रियलस्ट्रोकेशन पर मूव कंस्ट्रक्टर का उपयोग नहीं करना चाहिए, लेकिन इसके बारे में कुछ भी नहीं पता होना चाहिए

template<class T>
class move_only : public T{
public:
   move_only(){}
   move_only(const move_only&) = delete;
   move_only(move_only&&) noexcept {};
   ~move_only() noexcept {};

   using T::T;   
};

लाइव

या

template<class T>
struct move_only{
   T value;

   template<class Arg, class ...Args, typename = std::enable_if_t<
            !std::is_same_v<move_only<T>&&, Arg >
            && !std::is_same_v<const move_only<T>&, Arg >
    >>
   move_only(Arg&& arg, Args&&... args)
      :value(std::forward<Arg>(arg), std::forward<Args>(args)...)
   {}

   move_only(){}
   move_only(const move_only&) = delete;   
   move_only(move_only&& other) noexcept : value(std::move(other.value)) {};    
   ~move_only() noexcept {};   
};

लाइव कोड

आपकी Tकक्षा में noexceptकंस्ट्रक्टर / असिस्टेंट ऑपरेटर और noexceptडिस्ट्रक्टर होना चाहिए । अन्यथा आपको संकलन त्रुटि मिलेगी।

std::vector<move_only<MyClass>> vec;

1
कॉपी कंस्ट्रक्टर को हटाना आवश्यक नहीं है। यदि मूव कंस्ट्रक्टर नॉइसेप्ट है, तो इसका उपयोग किया जाएगा।
बाल्की

@ बाल्की इट मै का इस्तेमाल किया जाना चाहिए। मानक अब इसकी आवश्यकता नहीं है। यहाँ चर्चा है groups.google.com/a/isocpp.org/forum/...
tower120
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.