2017-02-01 14 views
0

У меня есть следующий образец:Что вызывает конструктор перемещения для удаления

#include <vector> 

class noncopyable { 
protected: 
    noncopyable() {} 
    ~noncopyable() {} 
    noncopyable(const noncopyable&) = delete; 
    noncopyable& operator=(const noncopyable&) = delete; 
    noncopyable(noncopyable&&) = default; 
    noncopyable& operator=(noncopyable&&) = default; 
}; 

class C1 : private noncopyable { 
public: 
    C1() { } 
    ~C1() { } 
}; 

int main() { 
    std::vector<C1> v; 
    v.emplace_back(); 
    return 0; 
} 

Я думал, что это должно работать, так как C1 должен быть подвижным, так как это базовый класс и не имеет элементов данных. Вместо этого я получил ошибки (с помощью лязга ++):

error: call to implicitly-deleted copy constructor of 'C1' 
    { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); } 
            ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
. 
. 
. 
note: in instantiation of function template specialization 'std::vector<C1, std::allocator<C1> >::emplace_back<>' requested here 
     v.emplace_back(); 
     ^
note: copy constructor of 'C1' is implicitly deleted because base class 'noncopyable' has a deleted copy constructor 
class C1 : private noncopyable { 
     ^
note: 'noncopyable' has been explicitly marked deleted here 
     noncopyable(const noncopyable&) = delete; 

Делая небольшое исследование (http://en.cppreference.com/w/cpp/language/move_constructor) показали, что если существует определенного пользователь деструктор, то нет неявного шага-конструктор не будет определен. Это, кажется, проблема здесь, поскольку C1 имеет деструктор, конструктор move не определяется. Конечно, если я либо удалю деструктор, либо добавлю C1(C1&&) = default; в C1, тогда он будет работать.

Пока все хорошо.

Проблема заключалась в том, что в сообщении об ошибке не упоминалось ~C1() или конструктор move. Он сказал, что пытается вызвать конструктор копирования, который был удален в базовом классе. Поэтому я попытался изменить функции delete ed в noncopyable вместо default ed и (сюрприз!), Который также решил ошибку.

Итак, мой вопрос в том, что делает последнее, что связано с ошибкой или ее исправлением? Если есть деструктор, в чем разница, если базовый класс имеет экземпляр-конструктор или нет?

+1

Попробуйте добавить спецификатор noexcept. – LogicStuff

+0

Обратите внимание, что существует разница между «не имеет конструктора перемещения» и «имеет конструктор удаленных перемещений». – aschepler

ответ

3

Вам не нужно vector, простой пример будет просто:

C1 a; 
C1 b(std::move(a)); // error: C1's copy constructor is deleted 

С [class.copy]:

Если определение класса X не явно объявить move, неявный будет объявлен неявным образом как дефолт, если и только если
(9.1) - X не имеет объявленного пользователем конструктора копирования,
(9 0,2) - X не имеет пользовательскую объявленную оператор присваивания копии,
(9.3) - X не имеет оператора присваивания, перемещение пользователя объявленных и
(9,4) - X не пользователь объявленного деструктор.

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

Если определение класса не явно объявить конструктор копирования не-явно один объявлено неявно. Если определение класса объявляет конструктор перемещения или перемещает оператор присваивания, неявно объявленный экземпляр конструктор определяется как удаленный; , в противном случае он определяется как дефолт (8.4). Последний случай устарел, если класс имеет пользовательский оператор присваивания копии или объявленный пользователем деструктор.

Полный набор конструкторов на C1, явных и неявных, выглядит следующим образом:

C1(); 
C1(C1 const&) = default; // but also delete 
~C1(); 

Так пытается построить C1 из RValue типа C1 будет соответствовать этому неявно объявленный конструктор копирования, как лучший (ничего более жизнеспособного), но этот конструктор - deleted, потому что noncopyable - это конструктор копий deleted, поэтому все выражение плохо сформировано.

Вот почему сообщение об ошибке упоминает конструктор. Это движение-строительство плохо сформировано, потому что наилучшим соответствием для этой конструкции перемещения является конструктор копирования плохо сформирован. Он не может упоминать конструктор перемещения, потому что нет конструктора перемещения, и деструктор не имеет отношения к выражению. Когда вы изменили базовый класс для копирования, теперь C1 также становится пригодным для копирования - поэтому ошибок нет. Там все еще нет конструктора движений, просто сейчас есть жизнеспособный кандидат на строительство.

+0

Как конструктор может быть как 'default', так и' delete'? Также, пожалуйста, объясните последнее предложение во второй цитате? Что значит, что дело «устарело»? Разве это не означает, что если у меня есть определяемый пользователем деструктор, я не получу неявный конструктор-копию (тот же, что и для конструктора move)? – baruch

+0

@baruch Это по умолчанию. Но значение по умолчанию плохо сформировано, поэтому оно удаляется. Устаревшее означает, что в какой-то неопределенной точке в будущем функциональность изменится. Не беспокойтесь об этом. Я просто включил его для полноты. – Barry

 Смежные вопросы

  • Нет связанных вопросов^_^