Insert into vector having objects without copy constructor
Solution 1
You should add move constructor - because std::vector::emplace_back
may do relocation which requires copy/move constructor. Or just use std::deque
.
#include <vector>
#include <deque>
using namespace std;
struct NoCopyNoMove
{
NoCopyNoMove(const NoCopyNoMove&) = delete;
NoCopyNoMove& operator=(const NoCopyNoMove&) = delete;
NoCopyNoMove(NoCopyNoMove&&) = delete;
NoCopyNoMove& operator=(NoCopyNoMove&&) = delete;
NoCopyNoMove(int){};
};
struct OnlyMove
{
OnlyMove(const OnlyMove&) = delete;
OnlyMove& operator=(const OnlyMove&) = delete;
OnlyMove(OnlyMove&&) noexcept {}
OnlyMove& operator=(OnlyMove&&) noexcept {}
OnlyMove(int){};
};
int main()
{
deque<NoCopyNoMove> x;
x.emplace_back(1);
vector<OnlyMove> y;
y.emplace_back(1);
}
§ 23.2.3 Table 101 — Optional sequence container operations
a.emplace_back(args)
[...]Requires:
T
shall beEmplaceConstructible
intoX
from args. Forvector
,T
shall also beMoveInsertable
intoX
.
Solution 2
The error is not the fault of emplace_back. To put an object in a vector it must be movable or copyable. If you actually run the code with copy constructor implemented you will notice it is never called. This is an entry on cppreference.com
What I would do to fix this is implement the move constructor, that makes it compile and I can't see any really drawback to having a move constructor. And as with the cctor the move constructor will not be called in your current code.
Solution 3
Just want to add to @kayleeFrye_onDeck's answer. I have a near identical situation to theirs, and the exact syntax that works for me (based on the feedback in the comments) is as follows:
vector< std::unique_ptr<ClassName> > names; // Declare vector of unique_ptrs of the class instance
std::unique_ptr<ClassName> name_ptr = std::make_unique<ClassName>();
names.push_back(std::move(name_ptr)); // Need to use std::move()
// Now you can access names objects without error:
names[0]->classMethod();
Related videos on Youtube
vigs1990
Updated on July 10, 2022Comments
-
vigs1990 almost 2 years
I have a class whose copy constructors are explicitly deleted (because A uses pointers internally and I don't want to fall into shallow copy pitfalls):
class A { public: A(const A&) = delete; A& operator=(const A&) = delete; A(const B& b, const C& c); }
Now I have a vector of type
vector<A> aVector;
and I want to insert elements into it - so I useemplace_back
:aVector.emplace_back(b, c);
However, this fails to compile using gcc and I get the error -
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_uninitialized.h:77:3: required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_uninitialized.h:119:41: required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_uninitialized.h:260:63: required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_uninitialized.h:283:67: required from '_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/vector.tcc:410:6: required from 'void std::vector<_Tp, _Alloc>::_M_emplace_back_aux(_Args&& ...) third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/vector.tcc:102:4: required from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...)
What is the reason for this error and how can it be fixed without removing the deletion of the copy constructors? Do I need a move constructor - does it need to be explicitly defined?
-
billz over 10 yearsHow do you define
A(const B& b, const C& c);
? -
WhozCraig over 10 yearsI've hit this same issue myself, and for me it was because of copy-elision fulfillment. The copy-ctor had to be provided, but was never called. I just tested your layout, and experienced the same issue your showing here using
clang-500.2.79
on my Mac. Providing a copy-ctor allowed it to compile, but never invoked the implemented copy-ctor. Likewise with providing a move-ctor. I'd have to dust off the question history to find the related question. Once you hit an expansion point in the vector, move-construction will be invoked if available, so you best add it regardless. -
Evgeny Panasyuk over 10 yearsYou should add move constructor - because
emplace_back
ofvector
may do relocation which requires copy/move constructor. Or just usestd::deque
.
-
-
WhozCraig over 10 years+1 that would support everything I've seen as well.
-
aaronman over 10 years@WhozCraig I would find a standard quote but sometimes that thing just gives me headaches
-
kayleeFrye_onDeck almost 7 years
std::deque
doesn't appear to work when the copy constructor from the class inherited from was deleted. I don't have access to the source I'm working with, but commenting out the use of adding objects from that class tostd::vector
andstd::deque
stops the error,"Error C2280 'ClassName::ClassNameConstructor(const ClassName &)': attempting to reference a deleted function"
Same goes forstd::list
-
kayleeFrye_onDeck almost 7 yearsHowever, it seems I was able to work around this with pointers. I made a pointer per object, and made a group of those pointers. Not the best workaround but I'll take what works!
-
aschepler almost 7 yearsBut if the lifetime of the vector is longer than the lifetime of the object you put in it, bang, you're dead.
-
kayleeFrye_onDeck almost 7 yearsYou sure are. But hey, I'm very open to alternatives :) I didn't find any... Thanks again for reminding me to add the warning to my answer.
-
M.M almost 7 yearsYou could improve your code by having a vector of
unique_ptr
which you created usingmake_unique
-
M.M almost 7 yearsReaders should note that the
noexcept
specifier on the move-constructor and move-assignment is important here -
kayleeFrye_onDeck almost 7 yearsIs this what you meant, @M.M by swapping it out with line 3 of the first snippet?
std::unique_ptr<ClassName> name_ptr = std::make_unique<ClassName>(name);
-
BeeOnRope over 6 yearsFWIW, the most recent version of
std::vector
documentation doesn't mention the requirement that elements are MoveConstructible or MoveAssignable any more. They only need to be Erasable.