@ Конрад, мне кажется, это слишком многословно, после того, как будет определен cctor, mctor не будет определен (насколько я понимаю текущий проект). Вы бы также определили конструктор по умолчанию как удаленный для каждого класса, который определяет пользовательский конструктор?

после просмотраэта замечательная лекция по ссылкам на rvalue я думал, что каждый класс получит пользу от такого «конструктора перемещения»,template<class T> MyClass(T&& other) редактировать и, конечно же, «оператор назначения перемещения»,template,<class T> MyClass& operator=(T&& other) Как указывает Филипп в своем ответе, если он имеет динамически распределяемых членов или вообще хранит указатели. Прямо как тыдолжен иметь copy-ctor, оператор присваивания и деструктор, если применяются указанные выше пункты. Мысли?

 kevinarpe23 авг. 2016 г., 16:56
Cripey. Ваша ссылка на "чудесную" лекцию .... эпическая. Я никогда не видел эту серию раньше.
 Aleks13 июл. 2016 г., 15:55
Некоторая дополнительная информация может быть найдена здесьbrookspatola.com/the-rule-of-five-c11 (просто копирование из удаленного ответа)

Ответы на вопрос(8)

Решение Вопроса

что правило трех становится правилом трех, четырех и пяти:

Каждый класс должен явно определять точно один из следующего набора специальных функций-членов:

НиктоДеструктор, конструктор копирования, оператор присваивания копии

Кроме того, каждый класс, который явно определяет деструктор, может явно определять конструктор перемещения и / или оператор присваивания перемещения.

Обычно целесообразно использовать один из следующих наборов специальных функций-членов:

Нет (для многих простых классов, где неявно генерируемые специальные функции-члены являются правильными и быстрыми)Деструктор, конструктор копирования, оператор присваивания копии (в этом случае класс не будет подвижным)Деструктор, конструктор перемещения, оператор присваивания перемещения (в этом случае класс не будет копируемым, полезно для классов управления ресурсами, где базовый ресурс не копируется)Деструктор, конструктор копирования, оператор присваивания копии, конструктор перемещения (из-за исключения копирования нет издержек, если оператор присваивания копии получает свой аргумент по значению)Деструктор, конструктор копирования, оператор назначения копирования, конструктор перемещения, оператор назначения перемещения

Обратите внимание, что конструктор перемещения и оператор присваивания перемещения не будут созданы для класса, который явно объявляет любую из других специальных функций-членов, этот конструктор копирования и оператор присваивания копии не будут созданы для класса, который явно объявляет конструктор перемещения или перемещение оператор присваивания, и что класс с явно объявленным деструктором и неявно определенным конструктором копирования или неявно определенным оператором присвоения копии считается устаревшим. В частности, следующий совершенно правильный C ++ 03 полиморфный базовый класс

class C {
  virtual ~C() { }   // allow subtype polymorphism
};

следует переписать следующим образом:

class C {
  C(const C&) = default;               // Copy constructor
  C(C&&) = default;                    // Move constructor
  C& operator=(const C&) = default;  // Copy assignment operator
  C& operator=(C&&) = default;       // Move assignment operator
  virtual ~C() { }                     // Destructor
};

Немного раздражает, но, вероятно, лучше, чем альтернатива (автоматическая генерация всех специальных функций-членов).

В отличие от правила «большой тройки», где несоблюдение этого правила может привести к серьезному ущербу, неявное объявление конструктора и оператора назначения перемещения в целом нормально, но часто неоптимально с точки зрения эффективности. Как упоминалось выше, конструктор перемещения и операторы присваивания перемещений генерируются только в том случае, если явно не объявлен конструктор копирования, оператор присваивания копии или деструктор. Это не симметрично традиционному поведению C ++ 03 в отношении автоматической генерации конструктора копирования и оператора назначения копирования, но намного безопаснее. Таким образом, возможность определять конструкторы перемещения и операторы присваивания перемещения очень полезна и создает новые возможности (чисто подвижные классы), но классы, которые придерживаются правила C ++ 03 «большой тройки», все еще будут в порядке.

Для классов управления ресурсами вы можете определить конструктор копирования и оператор назначения копирования как удаленные (что считается определением), если базовый ресурс не может быть скопирован. Часто вы все еще хотите переместить конструктор и оператор присваивания перемещения. Операторы назначения копирования и перемещения часто реализуются с использованиемswap, как в C ++ 03. Если у вас есть конструктор перемещения и оператор присваивания перемещения, специализирующийсяstd::swap станет неважным, потому что общийstd::swap использует конструктор перемещения и оператор присваивания перемещения, если он доступен, и это должно быть достаточно быстро.

Классы, которые не предназначены для управления ресурсами (то есть без непустого деструктора) или полиморфизма подтипа (то есть без виртуального деструктора) не должны объявлять ни одну из пяти специальных функций-членов; все они будут генерироваться автоматически и вести себя правильно и быстро.

 Mihai Todor06 сент. 2012 г., 21:00
@ BЈовић Нет, это не опечатка. Вот хорошее объяснение этому:stackoverflow.com/a/12306344/1174378
 Xeo24 янв. 2011 г., 15:33
@Philipp: Хм, верно ... операторы присваивания по значению 'other' будут созданы с помощью move, если вы просто внедрите move-ctor, если я правильно понял? А оставшиеся копии и назначения указателей будут оптимизированы компилятором, я думаю ...
 Philipp24 янв. 2011 г., 15:42
@Xeo: Я считаю, что если этот класс нельзя скопировать, вы не сможете передать его экземпляры по значению, даже если копия может быть исключена. В этом случае вы должны объявить истинный оператор присваивания перемещения, используя ссылку на rvalue (оператор присваивания, который принимает свой аргумент по значению, является оператором присваивания при копировании согласно §12.8 / 19, что вам не нужно, если класс не копируется). Для копируемых и перемещаемых классов компилятор должен использовать copy elision или вызывать конструктор move.
 Philipp08 февр. 2011 г., 20:50
@Omni: виртуальная функция не может быть явно задана по умолчанию при объявлении (§8.4.2 / 2 и последний пример в §8.4.2 / 5).
 Luc Danton21 сент. 2011 г., 20:18
Изменились ли правила с момента принятия C ++ 11? я верюstruct C { virtual ~C() = default; }; Теперь разрешен и самый лаконичный вариант. Запрет («- это не должно быть виртуальным») от n3242 больше не присутствует в n3290, и GCC разрешает это, в то время как ранее этого не было.

это.

В основном статья утверждает «Правило нуля». Мне неуместно цитировать всю статью, но я считаю, что это главное:

Классы, которые имеют собственные деструкторы, конструкторы копирования / перемещения или операторы назначения копирования / перемещения, должны иметь дело исключительно с владением. Другие классы не должны иметь пользовательских деструкторов, конструкторов копирования / перемещения или операторов назначения копирования / перемещения.

Также этот бит ИМХО важен:

Общие классы «владение в пакете» включены в стандартную библиотеку:std::unique_ptr а такжеstd::shared_ptr, Благодаря использованию пользовательских объектов удаления, оба были сделаны достаточно гибкими, чтобы управлять практически любым видом ресурса.

 Xeo19 дек. 2012 г., 13:33
ВидетьВот а такжеВот за мои мысли по этому вопросу. :)

правило трех это практическое правило, которое утверждает, что класс, который реализует одно из следующих, но не все из них, вероятно, глючит.

Копировать конструкторОператор присваиванияDestructor

Однако исключение конструктора перемещения или оператора присваивания перемещения не означает ошибку. Этомай быть упущенной возможностью при оптимизации (в большинстве случаев) или семантика перемещения не относится к этому классу, но это не ошибка.

Хотя может оказаться целесообразным определять конструктор перемещения, когда это уместно, это не обязательно. Во многих случаях конструктор перемещения не подходит для класса (например,std::complex) и все классы, которые ведут себя правильно в C ++ 03, будут продолжать работать правильно в C ++ 0x, даже если они не определяют конструктор перемещения.

я думаю, что было бы неплохо предоставить конструктор перемещения для таких классов, но помните, что:

Это всего лишь оптимизация.

Реализация только одного или двух из конструктора копирования, оператора присваивания или деструктора, вероятно, приведет к ошибкам, в то время как отсутствие конструктора перемещения только потенциально снизит производительность.

Конструктор Move не всегда может быть применен без изменений.

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

 Puppy24 янв. 2011 г., 15:17
Это не просто оптимизация, семантика перемещения важна для идеальной пересылки и некоторых классов (unique_ptr) не может быть реализовано без семантики перемещения.
 peoro24 янв. 2011 г., 15:21
@DeadMG: в целом вы правы, но в этом контексте семантика перемещения - это просто оптимизация. Здесь я говорю о уже существующих классах, которые уважают правило трех;unique_ptr и идеальная пересылка - это особые случаи ...
 Mooing Duck26 авг. 2011 г., 21:11
some classes have always their pointers allocated... в этом случае ход обычно реализуется как своп. Так же просто и быстро. (На самом деле быстрее, так как он перемещает освобождение в деструктор значения)
 Philipp24 янв. 2011 г., 15:26
@peoro: я думаю, что класс C ++ 03, который объявляет частный конструктор копирования и операторы копирования (или наследует отboost::noncopyable) можно призвать подчиняться правилу трех. (В противном случае мы должны ввести другую терминологию, например, «Правило Большого и Малого Два»).
 Philipp24 янв. 2011 г., 15:27
@DeadMG:unique_ptr уважает правило пяти.

если вы не объявляете какие-либо операции перемещения, вы должны соблюдать правило трех. Если вы объявляете операцию перемещения, не будет никакого вреда в «нарушении» правила трех, поскольку генерация операций, сгенерированных компилятором, стала очень строгой. Даже если вы не объявляете операции перемещения и нарушаете правило трех, ожидается, что компилятор C ++ 0x выдаст вам предупреждение в случае, если одна специальная функция была объявлена ​​пользователем, а другие специальные функции были сгенерированы автоматически из-за теперь устарело "Правило совместимости C ++ 03".

Я думаю, можно с уверенностью сказать, что это правило становится немного менее значимым. Настоящая проблема в C ++ 03 состоит в том, что для реализации другой семантики копирования требуется, чтобы пользователь объявлялвсе связанные специальные функции, так что ни одна из них не генерируется компилятором (что в противном случае сделало бы неправильно). Но C ++ 0x меняет правила генерации специальных функций-членов. Если пользователь объявляет только одну из этих функций для изменения семантики копирования, это не позволит компилятору автоматически генерировать оставшиеся специальные функции. Это хорошо, потому что отсутствующее объявление превращает ошибку времени выполнения в ошибку компиляции (или, по крайней мере, предупреждение). В качестве меры совместимости C ++ 03 некоторые операции все еще генерируются, но это поколение считается устаревшим и должно по крайней мере выдавать предупреждение в режиме C ++ 0x.

Из-за довольно ограничительных правил относительно специальных функций, сгенерированных компилятором, и совместимости с C ++ 03, правило трех остается правилом трех.

Вот некоторые примеры, которые могут подойти для новейших правил C ++ 0x:

template<class T>
class unique_ptr
{
   T* ptr;
public:
   explicit unique_ptr(T* p=0) : ptr(p) {}
   ~unique_ptr();
   unique_ptr(unique_ptr&&);
   unique_ptr& operator=(unique_ptr&&);
};

В приведенном выше примере нет необходимости объявлять какие-либо другие специальные функции как удаленные. Они просто не будут генерироваться из-за ограничительных правил. Наличие объявленных пользователем операций перемещения отключает сгенерированные компилятором операции копирования. Но в таком случае:

template<class T>
class scoped_ptr
{
   T* ptr;
public:
   explicit scoped_ptr(T* p=0) : ptr(p) {}
   ~scoped_ptr();
};

теперь ожидается, что компилятор C ++ 0x выдаст предупреждение о возможных операциях копирования, сгенерированных компилятором, которые могут сделать что-то не так. Здесь правило трех вопросов и должно соблюдаться. Предупреждение в этом случае абсолютно уместно и дает пользователю возможность справиться с ошибкой. Мы можем избавиться от проблемы с помощью удаленных функций:

template<class T>
class scoped_ptr
{
   T* ptr;
public:
   explicit scoped_ptr(T* p=0) : ptr(p) {}
   ~scoped_ptr();
   scoped_ptr(scoped_ptr const&) = delete;
   scoped_ptr& operator=(scoped_ptr const&) = delete;
};

Таким образом, правило трех все еще применяется здесь просто из-за совместимости C ++ 03.

 Philipp24 янв. 2011 г., 21:49
Фактически, N3126 определяет конструктор копирования и оператор присваивания копииunique_ptr как удалено - кто-нибудь знает почему?
 Philipp24 янв. 2011 г., 23:44
В N3126 действовали менее строгие правила, согласно которым конструктор копирования не будет неявно объявлен, если существует объявленный пользователем конструктор перемещения, и что оператор назначения копирования не будет объявлен неявно, если существует объявленный пользователем оператор назначения перемещения.unique_ptr имеет как объявленный пользователем конструктор перемещения, так и оператор присваивания перемещения, поэтому я думаю, что объявленный пользователем конструктор копирования и оператор назначения копирования не понадобятся даже при применении правил N3126. Не очень важно, но так как условные обозначения, используемые стандартными библиотечными классами, могут быть интерпретированы как лучшие
 sellibitze24 янв. 2011 г., 22:58
@ Филипп: Ограничительные правила новее, чем N3126. Тем не менее, N3225 по-прежнему объявляет операции копирования unique_ptr как удаленные. Это больше не нужно, но и не так. Таким образом, нет необходимости менять спецификацию unique_ptr.
 Philipp24 янв. 2011 г., 23:45
На практике было бы полезно узнать, являются ли явно объявленные конструктор копирования и оператор назначения копирования преднамеренными.

с 24 января 2011 года.

В соответствии со стандартом C ++ 11 (см. Приложение D [depr.impldec]):

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

Это было на самом делепредложил устареть устаревшее поведениедать C ++ 14 истинное «правило пяти» вместо традиционного «правила трех». В 2013 году EWG проголосовала против этого предложения, которое будет реализовано в C ++ 2014. Основное обоснование решения по предложению было связано с общими опасениями по поводу нарушения существующего кода.

Недавно это былопредложил еще раз адаптировать формулировку C ++ 11 для достижения неформального правила пяти, а именно

никакая функция копирования, функция перемещения или деструктор не могут быть сгенерированы компилятором, если любая из этих функций предоставлена ​​пользователем.

В случае одобрения EWG, «правило» может быть принято для C ++ 17.

 cb402 апр. 2016 г., 17:26
Спасибо за обновления. Поскольку некоторые из этих вопросов C ++ устаревают, полезно посмотреть, как на вопрос и / или ответы влияют новые языковые версии.

что правило 3 становится правилом 4 (или 5) сейчас, не нарушая весь существующий код, который реализует правило 3 и не реализует какую-либо форму семантики перемещения.

Правило 3 означает, что если вы реализуете одно, вы должны реализовать все 3.

Также не известно, будет ли автоматически сгенерированный ход. Цель «правила 3» состоит в том, что они автоматически существуют, и если вы реализуете одно, то, скорее всего, реализация двух других по умолчанию неверна.

тогда да, правило трех только что стало пятью, с добавлением оператора присваивания и конструктора перемещения. Однако, невсе классы являются копируемыми и перемещаемыми, некоторые просто перемещаемыми, некоторые просто копируемыми.

 Motti24 янв. 2011 г., 16:25
@ Филипп, я категорически не согласен, многие классы не поддерживают семантику перемещения, и нет смысла определять две избыточные функции только для некоторого эстетического восприятия. Почему следуетstd::complex заботиться о rvalue ссылки?
 Puppy24 янв. 2011 г., 16:33
@Motti: почему он определяет обычную семантику копирования? Практически все ресурсы, которые можно скопировать, можно переместить.
 Motti24 янв. 2011 г., 16:51
@ Конрад, мне кажется, это слишком многословно, после того, как будет определен cctor, mctor не будет определен (насколько я понимаю текущий проект). Вы бы также определили конструктор по умолчанию как удаленный для каждого класса, который определяет пользовательский конструктор?
 Philipp24 янв. 2011 г., 15:23
Я считаю, что даже если класс не копируемый, вы хотите определить конструктор копирования и оператор присваивания (как удаленный). Таким образом, класс управления подвижными ресурсами также должен определять все пять.
 Konrad Rudolph24 янв. 2011 г., 16:36
@Motti: Филипп сказал, что они должны быть определеныкак удалено! Таким образом, вы должны явно отрицать тот факт, что они не поддерживают операцию.

Ваш ответ на вопрос