Правило трех становится правилом пяти с C++11?



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

1013   8  

8 ответов:

Я бы сказал, что Правило трех становится правилом трех, четырех и пяти:

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

  • нет
  • деструктор, конструктор копирования, оператор присваивания копий

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

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

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

обратите внимание, что конструктор перемещения и оператор присваивания перемещения не будут сгенерированы для a класс, который явно объявляет любую из других специальных функций-членов, что конструктор копирования и оператор назначения копирования не будут созданы для класса, который явно объявляет конструктор перемещения или оператор назначения перемещения, и что класс с явно объявленным деструктором и неявно определенным конструктором копирования или неявно определенным оператором назначения копирования считается устаревшим. В частности, следующее совершенно действительное полиморфное основание 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 использует конструктор перемещения и оператор присваивания перемещения, если он доступен, и это должно быть достаточно быстро.

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

Я не могу поверить, что никто не связан с этой.

в основном статья утверждает "правило нуля". Мне не подходит цитировать всю статью, но я считаю, что это главное:

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

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

общие классы "владение в пакете" включены в стандарт библиотека: std::unique_ptr и std::shared_ptr. За счет использования пользовательские объекты deleter, оба были сделаны достаточно гибкими для управления практически любой вид ресурсов.

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

  1. конструктор копирования
  2. оператор присваивания
  3. деструктор

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

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

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

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

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

  • конструктор перемещения не всегда может быть применен без варианта исполнения.

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

вот краткое обновление текущего состояния и связанных с ним событий с 24 января '11.

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

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

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

в последнее время предложил снова адаптировать C++11 формулировка для достижения неофициального правила пяти, а именно:

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

в случае одобрения РГЭ "правило", скорее всего, будет принято для C++17.

В принципе, это так: Если вы не объявляете никаких операций перемещения, вы должны уважать правило трех. Если вы объявите операцию перемещения, нет никакого вреда в "нарушении" правила трех, поскольку генерация операций, созданных компилятором, стала очень ограничительной. Даже если вы не объявляете операции перемещения и нарушаете правило трех, компилятор 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();
};

a Теперь ожидается, что компилятор 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.

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

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

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

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

Comments

    Ничего не найдено.