Вставка в вектор, содержащий объекты без конструктора копии

У меня есть класс, конструкторы которого явно удалены (поскольку A использует указатели внутри, и я не хочу впадать в мелкие ошибки копирования):

class A {
  public:
    A(const A&) = delete;
    A& operator=(const A&) = delete;

    A(const B& b, const C& c);
}

Теперь у меня есть вектор типа vector<A> aVector;, и я хочу вставить в него элементы, поэтому я использую emplace_back:

aVector.emplace_back(b, c);

Однако это не скомпилируется с использованием gcc, и я получаю ошибку -

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&& ...)

В чем причина этой ошибки и как ее можно исправить, не удаляя удаление конструкторов копирования? Нужен ли мне конструктор перемещения - нужно ли его явно определять?

Ответ 1

Вы должны добавить конструктор перемещения - потому что std::vector::emplace_back может выполнять перемещение, для которого требуется конструктор копирования/перемещения. Или просто используйте 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.

Ответ 2

Ошибка не является ошибкой emplace_back. Чтобы поместить объект в вектор, он должен быть подвижным или скопированным. Если вы действительно запускаете код с реализованным конструктором копирования, вы заметите, что он никогда не вызывается. Это запись на cppreference.com
enter image description here

Что бы я сделал, чтобы исправить это, это реализовать конструктор перемещения, который заставляет его скомпилировать, и я не вижу никакого реального недостатка в создании конструктора перемещения. И как с помощью cctor, конструктор перемещения не будет вызываться в вашем текущем коде.

Ответ 3

Я столкнулся с этой проблемой с внешним классом библиотеки. Я получаю,

"Error C2280 ClassName::ClassName(const ClassName &)': attempting to reference a deleted function"

Я предполагаю, что класс, который я использовал, удалил его конструктор копирования. Я не мог добавить его в какие-либо контейнеры std, которые я знал для своих пользовательских объектов производного класса, которые обернули их объект некоторыми помощниками, чтобы помочь с проверками инициализации/ошибок.

Я работал над этим блокатором с помощью указателей (рискованных).

В основном, я перешел к этому:

std::vector<ClassName*> names;
ClassName name("arg");
ClassName name_ptr = &name;
names.push_back(name_ptr);

изначально:

std::vector<ClassName> names;
ClassName name("arg");
names.push_back(name);

Интересно сказать, что это была первая кодировка с С++, на которой мне действительно нужно было использовать указатели для нестандартных требований к использованию из-за отсутствия известной альтернативы. Это заставляет меня беспокоиться, что я могу пропустить что-то фундаментальное в своем собственном коде.

Может быть, есть лучший способ сделать это, но он еще не включен в этот список вопросов...

edit для Caveat:

Я должен был упомянуть об этом раньше, спасибо aschepler; если вы это сделаете, и контейнер, который вы используете, переживает объект, "bang, вы мертвы".

Ответ 4

Просто хочу добавить к @kayleeFrye_onDeck ответ. У меня ситуация, почти идентичная их, и точный синтаксис, который мне подходит (на основе отзывов в комментариях), выглядит следующим образом:

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();