वेक्टर <संरचना> पर C ++ 11 emplace_back?


89

निम्नलिखित कार्यक्रम पर विचार करें:

#include <string>
#include <vector>

using namespace std;

struct T
{
    int a;
    double b;
    string c;
};

vector<T> V;

int main()
{
    V.emplace_back(42, 3.14, "foo");
}

यह काम नहीं करता है:

$ g++ -std=gnu++11 ./test.cpp
In file included from /usr/include/c++/4.7/x86_64-linux-gnu/bits/c++allocator.h:34:0,
                 from /usr/include/c++/4.7/bits/allocator.h:48,
                 from /usr/include/c++/4.7/string:43,
                 from ./test.cpp:1:
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = T; _Args = {int, double, const char (&)[4]}; _Tp = T]’:
/usr/include/c++/4.7/bits/alloc_traits.h:253:4:   required from ‘static typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = T; _Args = {int, double, const char (&)[4]}; _Alloc = std::allocator<T>; typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type = void]’
/usr/include/c++/4.7/bits/alloc_traits.h:390:4:   required from ‘static void std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = T; _Args = {int, double, const char (&)[4]}; _Alloc = std::allocator<T>]’
/usr/include/c++/4.7/bits/vector.tcc:97:6:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int, double, const char (&)[4]}; _Tp = T; _Alloc = std::allocator<T>]’
./test.cpp:17:32:   required from here
/usr/include/c++/4.7/ext/new_allocator.h:110:4: error: no matching function for call to ‘T::T(int, double, const char [4])’
/usr/include/c++/4.7/ext/new_allocator.h:110:4: note: candidates are:
./test.cpp:6:8: note: T::T()
./test.cpp:6:8: note:   candidate expects 0 arguments, 3 provided
./test.cpp:6:8: note: T::T(const T&)
./test.cpp:6:8: note:   candidate expects 1 argument, 3 provided
./test.cpp:6:8: note: T::T(T&&)
./test.cpp:6:8: note:   candidate expects 1 argument, 3 provided

ऐसा करने का सही तरीका क्या है और क्यों?

(सिंगल और डबल ब्रेसेस भी आजमाए)


4
यदि आप एक उपयुक्त कंस्ट्रक्टर प्रदान करते हैं तो यह काम करेगा।
क्रिस

3
क्या इसका उपयोग करने के लिए स्वचालित रूप से बनाए गए ब्रेस स्ट्रक्चर कंस्ट्रक्टर के साथ इन-प्लेस का निर्माण करने का एक तरीका है T t{42,3.14, "foo"}?
एंड्रयू तोमाज़ोस

4
मुझे नहीं लगता है कि एक निर्माता का रूप लेता है। यह प्रारंभिक आरंभ है।
क्रिस


5
मैं किसी भी तरह से आपकी राय को प्रभावित करने की कोशिश नहीं कर रहा हूँ .. लेकिन अगर आपने कुछ समय से पतले प्रश्न पर ध्यान नहीं दिया है .. तो स्वीकृत उत्तर, इसके लेखक के पूरे सम्मान के साथ, आपके प्रश्न का उत्तर नहीं है और पाठकों को गुमराह कर सकता है।
हमाम हेलफावी

जवाबों:


19

भविष्य से किसी के लिए, यह व्यवहार C ++ 20 में बदल दिया जाएगा

दूसरे शब्दों में, भले ही आंतरिक रूप से कार्यान्वयन अभी भी कहेंगे T(arg0, arg1, ...)इसे नियमित रूप से माना जाएगा T{arg0, arg1, ...}जो आप उम्मीद करेंगे।


97

आपको कक्षा के लिए स्पष्ट रूप से एक ctor परिभाषित करने की आवश्यकता है:

#include <string>
#include <vector>

using namespace std;

struct T
{
    int a;
    double b;
    string c;

    T(int a, double b, string &&c) 
        : a(a)
        , b(b)
        , c(std::move(c)) 
    {}
};

vector<T> V;

int main()
{
    V.emplace_back(42, 3.14, "foo");
}

उपयोग emplace_backकरने का बिंदु एक अस्थायी वस्तु बनाने से बचने के लिए है, जिसे तब गंतव्य पर कॉपी (या स्थानांतरित) किया जाता है। हालांकि यह एक अस्थायी वस्तु बनाने के लिए भी संभव है, फिर emplace_backइसे पास करें , यह कम से कम (कम से कम अधिकांश) उद्देश्य को हरा देता है। आप जो करना चाहते हैं वह व्यक्तिगत तर्कों को पारित करने के लिए है, फिर emplace_backवस्तु को बनाने के लिए उन तर्कों के साथ ctor को लागू करें।


12
मुझे लगता है कि लिखने का बेहतर तरीका होगाT(int a, double b, string c) : a(a), b(b), c(std::move(c))
बाल्की

9
स्वीकृत उत्तर के उद्देश्य को पराजित करता है emplace_back। यह सही जवाब है। यह कैसा emplace*काम है। वे अग्रेषित तर्कों का उपयोग करते हुए तत्व का निर्माण करते हैं। इसलिए, उक्त तर्कों को लेने के लिए एक रचनाकार की आवश्यकता होती है।
अंडरस्कोर_ड

1
अभी भी, वेक्टर एक emplace_aggr, सही प्रदान कर सकता है?
tamas.kenez

@balki ठीक है, वहाँ लेने कोई मतलब नहीं है cद्वारा &&कुछ भी नहीं इसके संभावित rvalueness के साथ किया जाता है; सदस्य के आरंभीकरण पर, तर्क को एक कलाकार की अनुपस्थिति में फिर से एक अंतराल के रूप में माना जाता है, इसलिए सदस्य को केवल कॉपी-निर्माण मिलता है। यहां तक ​​कि अगर सदस्य स्थानांतरित कर दिया गया था, तो यह अस्थायी या std::move()घ lvalue पास करने के लिए हमेशा कॉलर्स की आवश्यकता नहीं होती है (हालांकि मैं यह स्वीकार करूंगा कि मेरे कोड में कुछ कोने-मामले हैं जहां मैं ऐसा करता हूं, लेकिन केवल कार्यान्वयन विवरण में) ।
अंडरस्कोर_ड

26

बेशक, यह एक जवाब नहीं है, लेकिन यह ट्यूपल्स की एक दिलचस्प विशेषता को दर्शाता है:

#include <string>
#include <tuple>
#include <vector>

using namespace std;

using T = tuple <
    int,
    double,
    string
>;

vector<T> V;

int main()
{
    V.emplace_back(42, 3.14, "foo");
}

9
यह निश्चित रूप से एक जवाब नहीं है। इसके बारे में क्या दिलचस्प है? ctor के साथ किसी भी प्रकार को इस तरह से खाली किया जा सकता है। tuple में एक ctor होता है। सेशन की संरचना नहीं की। कि इस सवाल का जवाब।
अंडरस्कोर_ड

6
@underscore_d: मुझे यकीन नहीं है कि मैं 3 ago साल पहले जो मैं सोच रहा था, उसके हर विवरण को याद करता हूं, लेकिन मैंने जो सुझाव दिया था, मैं उसे पतला कर रहा था कि यदि आप tupleएक पीओडी संरचना को परिभाषित करने के बजाय इसका उपयोग करते हैं , तो आपको मुफ्त में एक कंस्ट्रक्टर मिल जाएगा , जिसका अर्थ है कि आपको emplaceसिंटैक्स मुफ्त में मिलता है (अन्य चीजों के अलावा - आपको लेक्सिकोग्राफिक ऑर्डर भी मिलता है)। आप सदस्य नामों को खो देते हैं, लेकिन कभी-कभी यह कम परेशान करने वाले एक्सेसरीज़ बनाता है जो कि आपके द्वारा आवश्यक बॉयलरप्लेट के बाकी हिस्सों की तुलना में होता है। मैं इस बात से सहमत हूं कि जेरी कॉफिन का जवाब स्वीकार किए गए से बेहतर है। मैंने इसे वर्षों पहले भी उकेरा था।
रिसी

3
हाँ, इसे वर्तनी से मुझे यह समझने में मदद मिलती है कि आपका क्या मतलब है! अच्छी बात। मैं मानता हूं कि कभी-कभी सामान्यीकरण मुस्कराते हुए होता है, जब एसटीएल दूसरे सामान के मुकाबले तौला जाता है जो हमारे लिए प्रदान करता है: मैं उस अर्ध-अक्सर का उपयोग करता हूं pair... लेकिन कभी-कभी आश्चर्य होता है कि क्या मैं वास्तव में शुद्ध शब्दों में बहुत कुछ हासिल करता हूं, हे। लेकिन शायद tupleभविष्य में पालन करेंगे। विस्तार के लिए धन्यवाद!
अंडरस्कोर_ड

12

यदि आप कंस्ट्रक्टर नहीं जोड़ना चाहते (या नहीं बना सकते), T के लिए एलोकेशन विशेषज्ञ (या अपना खुद का एलोकेटर बनाएं)।

namespace std {
    template<>
    struct allocator<T> {
        typedef T value_type;
        value_type* allocate(size_t n) { return static_cast<value_type*>(::operator new(sizeof(value_type) * n)); }
        void deallocate(value_type* p, size_t n) { return ::operator delete(static_cast<void*>(p)); }
        template<class U, class... Args>
        void construct(U* p, Args&&... args) { ::new(static_cast<void*>(p)) U{ std::forward<Args>(args)... }; }
    };
}

नोट: ऊपर दिखाया गया सदस्य फ़ंक्शन कंस्ट्रक्शन 3.1 के साथ संकलन नहीं कर सकता (क्षमा करें, मुझे नहीं पता कि क्यों)। अगले एक का प्रयास करें यदि आप क्लैंग 3.1 (या अन्य कारणों) का उपयोग करेंगे।

void construct(T* p, int a, double b, const string& c) { ::new(static_cast<void*>(p)) T{ a, b, c }; }

अपने आवंटित कार्य में, आपको संरेखण के बारे में चिंता करने की आवश्यकता नहीं है? देखेंstd::aligned_storage
एंड्रयू टॉमाज़ोस

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

6

यह 23.2.1 / 13 में कवर किया गया लगता है।

सबसे पहले, परिभाषाएँ:

एक कंटेनर प्रकार एक्स को देखते हुए एक ए के समान एक एलोकेटर_टाइप और टी के समान एक वैल्यू_टाइप दिया गया है और टाइप ए का एक लैवल्यू मीटर, टाइप टी * का सूचक पी, टाइप टी का एक अभिव्यक्ति वी, और टाइप टी का एक आरवीयू वीवी दिया गया है। निम्नलिखित शर्तें परिभाषित की गई हैं।

अब, यह क्या बनाता है emplace- रचनात्मक:

T, ज़ीरो या अधिक दलीलों के तर्क के लिए X से EmplaceConstructible है, जिसका अर्थ है कि निम्नलिखित अभिव्यक्ति अच्छी तरह से बनाई गई है: एलोकेटर_ट्रिट्स :: निर्माण (m, p, args);

और अंत में निर्माण कॉल के डिफ़ॉल्ट कार्यान्वयन के बारे में एक नोट:

नोट: एक कंटेनर आवंटितकर्ता_ट्रेट्स को कॉल करता है :: निर्माण (एम, पी, आर्ग्स) पी पर एक तत्व का निर्माण करने के लिए आर्ग्स का उपयोग कर। एसटीडी में डिफ़ॉल्ट निर्माण :: आवंटनकर्ता :: नई ((शून्य *) पी) टी (आर्ग्स) को कॉल करेगा, लेकिन विशेष आवंटनकर्ता एक अलग परिभाषा चुन सकते हैं।

यह बहुत कुछ हमें बताता है कि एक डिफ़ॉल्ट (और संभवतः एकमात्र) आवंटन योजना के लिए आपने एक निर्माता को उस चीज़ के लिए उचित तर्क के साथ परिभाषित किया होगा जिसे आप कंटेनर में फेंकने की कोशिश कर रहे हैं।


-2

आपको अपने प्रकार के लिए एक कंस्ट्रक्टर को परिभाषित करना होगा Tक्योंकि इसमें ए शामिल हैstd::string जो तुच्छ नहीं है।

इसके अलावा, यह परिभाषित करना बेहतर होगा (संभावित डिफ़ॉल्ट) चाल ctor / असाइन करें (क्योंकि आपके पास std::stringसदस्य के रूप में एक चल है ) - यह आपके स्थानांतरित करने में मदद करेगाT बहुत अधिक कुशल होने में ...

या, केवल पड़ोसी की प्रतिक्रिया में अनुशंसित T{...}अतिभारित कॉल का उपयोग करें emplace_back()... सब कुछ आपके विशिष्ट उपयोग के मामलों पर निर्भर करता है ...


एक मूव कंस्ट्रक्टर स्वचालित रूप से T
एंड्रयू टोमाज़ोस

1
@ AndrewTomazos-Fathomling: केवल अगर कोई उपयोगकर्ता ctors परिभाषित नहीं कर रहे हैं
zaufi

1
सही है, और वे नहीं हैं।
एंड्रयू तोमाज़ोस

@ AndrewTomazos-Fathomling: लेकिन आपको emplace_back()कॉल पर अस्थायी उदाहरण से बचने के लिए कुछ को परिभाषित करना होगा :)
zaufi

1
वास्तव में गलत है। एक चाल निर्माणकर्ता स्वचालित रूप से उत्पन्न होता है बशर्ते कोई विध्वंसक, प्रतिलिपि निर्माता या असाइनमेंट ऑपरेटर परिभाषित न हों। Emplace_back के साथ उपयोग के लिए एक 3 तर्क सदस्य के निर्माण को परिभाषित करने से डिफ़ॉल्ट चाल निर्माणकर्ता को दबाया नहीं जाएगा।
एंड्रयू टॉमाज़ोस

-2

आप struct Tउदाहरण बना सकते हैं और फिर इसे वेक्टर में स्थानांतरित कर सकते हैं :

V.push_back(std::move(T {42, 3.14, "foo"}));

2
आपको std :: move () एक अस्थायी ऑब्जेक्ट T {...} की आवश्यकता नहीं है। यह पहले से ही एक अस्थायी वस्तु (लकीर) है। तो आप बस अपने उदाहरण से std :: move () ड्रॉप कर सकते हैं।
नादव हर'एल

इसके अलावा, यहां तक ​​कि टाइप नाम टी आवश्यक नहीं है - संकलक यह अनुमान लगा सकता है। तो बस एक "V.push_back {42, 3.14," फू "}" काम करेगा।
नादव हर'एल

-8

{}नए तत्व को आरंभ करने के लिए आप सिंटैक्स का उपयोग कर सकते हैं :

V.emplace_back(T{42, 3.14, "foo"});

यह अनुकूलित किया जा सकता है या नहीं भी हो सकता है, लेकिन यह होना चाहिए

आपको काम करने के लिए एक कंस्ट्रक्टर को परिभाषित करना होगा, ध्यान दें कि आपके कोड के साथ भी आप ऐसा नहीं कर सकते हैं:

T a(42, 3.14, "foo");

लेकिन यह वही है जो आपको काम करने की आवश्यकता है।

बस ऐसे:

struct T { 
  ...
  T(int a_, double b_, string c_) a(a_), b(b_), c(c_) {}
}

यह वांछित तरीके से काम करेगा।


10
क्या यह एक अस्थायी निर्माण करेगा और फिर इसे सरणी में निर्माण करेगा? - या यह आइटम का निर्माण करेंगे?
एंड्रयू तोमाज़ोस

3
std::moveआवश्यक नहीं है। T{42, 3.14, "foo"}पहले से ही emplace_back द्वारा अग्रेषित किया जाएगा और एक मूवमेंट के रूप में स्ट्रक्चर मूव कंस्ट्रक्टर को बाँध देगा। हालाँकि मैं एक ऐसे समाधान को प्राथमिकता दूंगा जो इसमें जगह बनाए।
एंड्रयू टॉमाज़ोस

37
इस मामले में चाल लगभग कॉपी के बराबर है, इसलिए अनुकरण का पूरा बिंदु छूट गया है।
एलेक्स आई।

5
@AlexI। वास्तव में! यह सिंटैक्स एक अस्थायी बनाता है, जिसे 'emplace_back' के तर्क के रूप में पारित किया जाता है। पूरी तरह से बात याद आती है।
aldo

5
मुझे सभी नकारात्मक प्रतिक्रिया समझ में नहीं आती है। इस मामले में संकलक आरवीओ का उपयोग नहीं करेंगे?
यूरी पिनहोलो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.