Используйте конструктор копирования, пока присутствует объявленный пользователем конструктор перемещения

#c #class #copy-constructor #copy-assignment

#c #класс #copy-constructor #копирование-присвоение

Вопрос:

У меня возникает некоторая путаница при использовании конструктора копирования и оператора присваивания копирования при работе unique_ptr . Я был бы очень признателен, если бы вы могли поделиться некоторыми соображениями!

Теперь у нас есть class B .
Struct C является членом class B .
Struct C имеет std::unique_ptr<A> член.

A.h

 class A {
 public:
  A(int* id);
  A(const Aamp; other);
  ~A() override;

 private:
  Microsoft::WRL::ComPtr<int> id_;
};
 

A.cpp

 A::A(int* id) : id_(id) {
}

A::A(
    const Aamp; other) {
  id_ = other.id_;
}

A::~A() = default;
 

B.h

 class B {
 public:
  struct C{
   public:
    C(
        int input_pad_id,
        Microsoft::WRL::ComPtr<int> input_id,
        std::unique_ptr<A>amp; input_a);
    C(const Camp; other);
    Camp; operator=(const Camp;);
    ~C();

    int pad_id;
    Microsoft::WRL::ComPtr<int> id;
    std::unique_ptr<A> a;
  };

  B();
  B(const Bamp;) = delete;
  Bamp; operator=(const Bamp;) = delete;
  ~B() override;

 private:
  std::vector<C> c_item_;
};
 

B.cpp

 B::B() = default;

B::~B() = default;

/* omitting some code related to operation logic */
...
c_item_.push_back({pad_id, id, nullptr});
...
/* omitting some code related to operation logic */

B::C::C(int input_pad_id,
        Microsoft::WRL::ComPtr<int> input_id,
        std::unique_ptr<A>amp; input_a)
    : pad_id(input_pad_id), id(input_id),
      a(std::move(input_a)) {}

B::C::~C() = default;

B::C::C(const Camp; other) = default;

B::Camp; B::C::operator=(const B::Camp; other) = default;
 

При сборке я получил этот журнал ошибок:

 error: no matching member function for call to 'push_back'
  c_item_.push_back({pad_id, id, nullptr});
  ~~~~~~~~~~^~~~~~~~~
../../buildtools/third_party/libc  /trunk/includevector(711,36): note: candidate function not viable: cannot convert initializer list argument to 'const std::__vector_base<B::C, std::allocator<B::C>>::value_type' (aka 'const B::C')
    _LIBCPP_INLINE_VISIBILITY void push_back(const_reference __x);
                                   ^
../../buildtools/third_party/libc  /trunk/includevector(714,36): note: candidate function not viable: cannot convert initializer list argument to 'std::vector<B::C>::value_type' (aka 'B::C')
    _LIBCPP_INLINE_VISIBILITY void push_back(value_typeamp;amp; __x);
                                   ^
../B.cc(311,5): error: defaulting this copy constructor would delete it after its first declaration
    B::C::C(const Camp; other) = defau<
       ^
../B.h(33,46): note: copy constructor of 'C' is implicitly deleted because field 'c_item' has a deleted copy constructor
    std::unique_ptr<C> c_item;

../../buildtools/third_party/libc  /trunk/includememory(2528,3): note: copy constructor is implicitly deleted because 'unique_ptr<B>' has a user-declared move constructor
  unique_ptr(unique_ptramp;amp; __u) _NOEXCEPT
  ^
../B.cc(315,65): error: defaulting this copy assignment operator would delete it after its first declaration
    B::Camp; B::C::operator=(const B::Camp; other) = defau<
                ^
../B.h(33,46): note: copy assignment operator of 'C' is implicitly deleted because field 'c_item' has a deleted copy assignment operator
    std::unique_ptr<A> a;

../../buildtools/third_party/libc  /trunk/includememory(2528,3): note: copy assignment operator is implicitly deleted because 'unique_ptr<A>' has a user-declared move constructor
  unique_ptr(unique_ptramp;amp; __u) _NOEXCEPT
  ^
 

В этом случае, как я мог бы решить эту ошибку компиляции, заставив код использовать конструктор копирования, который доступен, пока unique_ptr<A> имеет объявленный пользователем
конструктор перемещения?

Комментарии:

1. Если вам нужна unique_ptr оболочка, подобная оболочке, которая предоставляет возможности копирования, ее можно написать с нуля (или создать класс, содержащий a std::unique_ptr , но обеспечивающий копирование с помощью определяемого пользователем оператора копирования ctor / присваивания). Сложно заставить его работать во всех тех же ситуациях, unique_ptr что и a, но если это не нужно — нет проблем.

Ответ №1:

Позвольте мне удалить некоторое форматирование:

  error: defaulting this copy constructor would delete it...
  C(const Camp; other) = default;
 ...because copy constructor of 'C' is implicitly deleted because field 'c_item' has a deleted copy constructor
  std::unique_ptr<C> c_item;
 

std::unique_ptr не имеет конструктора копирования, потому что он уникален. Его нельзя скопировать. Таким образом, конструктор копирования по умолчанию для всего, что содержит a std::unique_ptr , удаляется. Итак, в вашем C классе нет конструктора копирования. Кроме того, вы никогда не давали ему конструктор перемещения.

И std::vector методы вставки (aka push_back) могут иметь изменение размера, поэтому он должен перемещать или копировать свои элементы, но C не имеет конструктора копирования или перемещения, поэтому вы получаете ошибку компилятора.

Таким образом, решение состоит в том, чтобы предоставить C либо рабочий конструктор копирования, и / или конструктор перемещения. Если это имеет смысл для того, что у вас C есть, вы могли бы предоставить ему конструктор копирования, создав глубокую копию объекта, на который указано A , но я не знаю, что A это такое, поэтому я не могу сказать наверняка. Однако почти всегда имеет смысл создать конструктор перемещения.

Ответ №2:

 template<typename X, typename D=std::default_deleter<A>, typename base=std::unique_ptr<A,D>>
struct not_unique_ptr
:   base {
    static_assert(std::is_same_v<base,std::unique_ptr<A,D>>);
    not_unique_ptr()=default;
    not_unique_ptr(not_unique_ptr amp;amp;) = default;
    not_unique_ptr(not_unique_ptr constamp; init)
    :   base{std::make_unique<A>(*init)};
    explicit not_unique_ptr(base amp;amp; b) 
    :   base{std::move(b)}{};
    autoamp; operator=(not_unique_ptr amp;amp;) = default;
    autoamp; operator=(baseamp;amp; rhs){
        baseamp;{*this}=std::move(rhs);
    };
    autoamp; operator=(not_unique_ptr constamp; rhs){
        baseamp;{*this}=baseamp;amp;{not_unique_ptr{rhs}};
    };
};