Insert into vector having objects without copy constructor

22,763

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.

LIVE DEMO

#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 be EmplaceConstructible into X from args. For vector, T shall also be MoveInsertable into X.

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
enter image description here

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();
Share:
22,763

Related videos on Youtube

vigs1990
Author by

vigs1990

Updated on July 10, 2022

Comments

  • vigs1990
    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 use emplace_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
      billz over 10 years
      How do you define A(const B& b, const C& c); ?
    • WhozCraig
      WhozCraig over 10 years
      I'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
      Evgeny Panasyuk over 10 years
      You should add move constructor - because emplace_back of vector may do relocation which requires copy/move constructor. Or just use std::deque.
  • WhozCraig
    WhozCraig over 10 years
    +1 that would support everything I've seen as well.
  • aaronman
    aaronman over 10 years
    @WhozCraig I would find a standard quote but sometimes that thing just gives me headaches
  • kayleeFrye_onDeck
    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 to std::vector and std::deque stops the error, "Error C2280 'ClassName::ClassNameConstructor(const ClassName &)': attempting to reference a deleted function" Same goes for std::list
  • kayleeFrye_onDeck
    kayleeFrye_onDeck almost 7 years
    However, 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
    aschepler almost 7 years
    But if the lifetime of the vector is longer than the lifetime of the object you put in it, bang, you're dead.
  • kayleeFrye_onDeck
    kayleeFrye_onDeck almost 7 years
    You 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
    M.M almost 7 years
    You could improve your code by having a vector of unique_ptr which you created using make_unique
  • M.M
    M.M almost 7 years
    Readers should note that the noexcept specifier on the move-constructor and move-assignment is important here
  • kayleeFrye_onDeck
    kayleeFrye_onDeck almost 7 years
    Is 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
    BeeOnRope over 6 years
    FWIW, 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.