2013-03-25 21 views
16

Mam następujący kod:dlaczego emplace_back musi przejść konstruktora

#include <string> 
#include <vector> 
#include <iostream> 

class Test final { 
public: 
    Test(const std::string& s) 
    : s_(s) { 
    std::cout << "constructing: " << s_ << std::endl; 
    } 
#ifdef NO_MOVE 
private: 
    Test(const Test& t) = delete; 
    Test(Test&& t) = delete; 
#else 
public: 
    Test(const Test& t) 
    : s_(t.s_) { 
    std::cout << "copying: " << s_ << std::endl; 
    }; 
    Test(Test&& t) 
    : s_(std::move(t.s_)) { 
    std::cout << "moving: " << s_ << std::endl; 
    }; 
#endif 
private: 
    std::string s_; 
}; 

int main() { 
    std::vector<Test> v; 
    v.emplace_back("emplace_back"); 
} 

Kiedy konstruktor ruch jest dozwolony, zachodzą następujące zdarzenia:

[matt test] g++ -std=c++11 main.cpp && ./a.out 
constructing: emplace_back 

Jednakże, jeśli konstruktor ruch jest usuwany:

[matt test] g++ -std=c++11 main.cpp -DNO_MOVE && ./a.out 
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = Test; _Args = {Test}]’: 
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:77:3: required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<Test*>; _ForwardIterator = Test*; bool _TrivialValueTypes = false]’ 
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:119:41: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<Test*>; _ForwardIterator = Test*]’ 
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:260:63: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<Test*>; _ForwardIterator = Test*; _Tp = Test]’ 
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:283:69: required from ‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = Test*; _ForwardIterator = Test*; _Allocator = std::allocator<Test>]’ 
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/vector.tcc:410:6: required from ‘void std::vector<_Tp, _Alloc>::_M_emplace_back_aux(_Args&& ...) [with _Args = {const char (&)[13]}; _Tp = Test; _Alloc = std::allocator<Test>]’ 
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/vector.tcc:102:4: required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {const char (&)[13]}; _Tp = Test; _Alloc = std::allocator<Test>]’ 
main.cpp:32:32: required from here 
main.cpp:14:3: error: ‘Test::Test(Test&&)’ is private 
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/vector:63:0, 
       from main.cpp:2: 
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_construct.h:77:7: error: within this context 
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_construct.h:77:7: error: use of deleted function ‘Test::Test(Test&&)’ 
main.cpp:14:3: error: declared here 

Ale emplace_back nie korzysta z konstruktora ruchu. Dlaczego inicjalizacja wymaga konstruktora ruchu w tym przypadku?

+0

Robi to z 'gcc 4.7.2' –

+0

dobrze, jesteś również defing się z kopia-konstruktor, emplace-back musi albo kopiowaniem lub move-konstruktor. – inf

+7

Niepoprawnie, nie powinno to być potrzebne dla samej postawy. Jednakże, dla odrastania, 'T' musi być co najmniej' MoveConstructible', co jest, IIRC, również ogólnym wymaganiem dla 'std :: vector'. – Xeo

Odpowiedz

19

Jak podano w komentarzu po pytaniu. Operator emplace_back może być zmuszony do ponownego przydzielenia pamięci kontenerów i jako taki typ szablonu musi być albo kopiowany, albo przenośny.

To nie jest przekazywanie argumentów, ale problem polega na przydzielaniu pamięci dla nowego obiektu.

+1

Pamiętaj również o 'noexcept' dla konstruktora ruchu, ponieważ nie jest bezpiecznie przenosić wiele elementów nie atomowo. –

+0

Dobra uwaga. Pamiętaj, aby dodać 'noexcept' –

+0

Tylko dla odniesienia, jest to wywoływane w Tabeli 101 standardu C++ 11," Dla wektora, T będzie również MoveInsertable na X ". –

1

Jeśli nie ma miejsca w wektorze to powinno przydzielić nową przestrzeń i przenieść tam wszystko i uniknąć egzemplarz zasobu zawarty lub posiadanych przez obiekty (wewnątrz wektora) ruch jest wymagane

Dla wektora typu Test

Test object(original)--->resource on heap 
Test object(relocated with move constructor)------>resource on heap 
Test object(relocated with copy constructor)------>copy of resource on heap 
Powiązane problemy