emplace_back - c++ emplace back
¿Por qué emplace_back necesita mover constructor? (2)
Como se especifica en el comentario después de la pregunta. El operador emplace_back
puede necesitar reasignar la memoria de los contenedores y, como tal, el tipo de plantilla vector
debe ser emplace_back
o emplace_back
.
No es el reenvío de los argumentos que es el problema, es la asignación de memoria para el nuevo objeto.
Tengo el siguiente código:
#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");
}
Cuando se permite un constructor de movimiento, ocurre lo siguiente:
[matt test] g++ -std=c++11 main.cpp && ./a.out
constructing: emplace_back
Sin embargo, si se quita el constructor de movimiento:
[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
Pero el emplace_back
no usa el constructor de movimiento. ¿Por qué la inicialización requiere un constructor de movimiento en esta instancia?
Si no hay espacio en el vector, entonces debe asignar un nuevo espacio y mover todo allí y para evitar la copia del recurso contenido o propiedad de los objetos (dentro del vector), se requiere el movimiento.
Para vectores de tipo 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