2016-05-17 3 views
10

В соответствии со стандартом,По умолчанию шаг конструктор/присваивание и удален конструктор копирования/назначение

Если определение класса X не явно объявить конструктор перемещения, один будет неявно объявлен дефолт, если и только если

- X не имеет пользовательскую объявленную конструктор копирования,

- X не имеет оператора присваивания копии пользователя заявил,

- X не ч ave пользовательский оператор присваивания оператора и

- X не имеет объявленного пользователем деструктора.

Теперь следующий не удается скомпилировать

# include <utility> 

class Foo 
{ 
public: 
    Foo() = default; 
    Foo(Foo const &) = delete; 
}; 

int main() 
{ 
    Foo f; 
    Foo g(std::move(f)); // compilation fails here 
    return 0; 
} 

Таким образом, кажется, что удаленная функция рассматриваются как определенный пользователь, который имеет смысл (это не его реализация по умолчанию). Однако, в этом конкретном случае, как бы удалить конструктор/назначение перемещения по умолчанию constorutor/assign mess default?

Я думаю, что этот вопрос имеет практическое значение, потому что ручная генерация и esp. поддержание таких функций по умолчанию является склонным к ошибкам, в то время как (праведное) увеличение использования классов, таких как std::unique_ptr в качестве членов класса, делало не скопируемые классы гораздо более распространенными животными, чем они были раньше.

+0

Не уверен, что хорошо понял ваш вопрос, но почему бы вам не поменять ход ctor? то есть 'Foo (Foo &&) = default;' –

ответ

12

user-declared означает либо либо предоставленный пользователем (определяется пользователем), явно дефолт (= default) или явно удалены (= delete), в отличии от неявно дефолта/удалено (например, вашего ход конструктора).

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

Однако, в этом конкретном случае, как бы удалить конструктор/назначение копирования беспорядок по умолчанию move constructor/assign?

Это не так, но стандарт не делает разницы между этим случаем и сложным.

Самый короткий ответ, что наличие неявно определенный Move-конструктор с в явном виде удалены от копирования конструктора может быть опасным в некоторых случаях, то же самое, когда у вас есть определяемые пользователем деструктор и нет пользовательский копировальный аппарат (см. rule of three/five/zero). Теперь вы можете утверждать, что определяемый пользователем деструктор не удаляет экземпляр-конструктор, но это просто недостаток на языке, который нельзя удалить, поскольку он сломает много старой (плохой) программы. Цитирую Бьерн Страуструп:

В идеальном мире, я думаю, мы бы решили на «нет поколения» по умолчанию и обеспечивают очень простое обозначение для [...] «дайте мне все обычные операции.» Кроме того, политика «без операций по умолчанию» приводит к ошибкам компиляции времени (которые мы должны иметь простой способ исправить), тогда как операция генерации по умолчанию приводит к проблемам, которые не могут быть обнаружены до времени выполнения.

Подробнее об этом можно узнать в N3174=10-0164.

Обратите внимание, что большинство людей следует за rule of three/five/zero, и, на мой взгляд, вам следует. При неявном удалении конструктора-указателя по умолчанию стандарт «защищает» вас от ошибок и должен был защитить вас задолго до этого, удалив в некоторых случаях экземпляр-копию (см. Статью Бьярне).

Дальнейшее чтение, если вы заинтересованы:

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

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

class Foo { 
public: 
    Foo() = default; 
    Foo(Foo const &) = delete; 
    Foo(Foo&&) = default; 
}; 

Вы получаете не-Copyable объект с конструктором перемещения по умолчанию, и, по моему мнению, эти явные заявления лучше, чем имплицитные (например, только объявляя конструктор перемещения как default без удаления экземпляра-копии).

+0

@NathanOliver Здесь есть некоторые http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3153.htm. – Holt

+0

Спасибо. Я тоже нашел. Это для меня лучший ответ. – NathanOliver

-1

Поведения по существу может быть проиллюстрирован:

void foo(const int& i) 
{ 
    std::cout << "copy"; 
} 

void foo(int&& i) 
{ 
    std::cout << "move"; 
} 

Когда оба перегрузок присутствуют, перегрузка int&& выбираются для rvalues ​​в то время перегрузка const int& выбрана для lvalues. Если вы удалите int&& перегрузку, то const int& называется (ergo, а не ошибка) даже для rvalues. Это, по сути, что происходит в этом случае: разрешение

class Foo 
{ 
public: 
    Foo() = default; 
    Foo(Foo const &) = delete; 
}; 

Перегрузки видит только одного кандидата, но так как он явно удален, программа плохо сформирована.

Не-нормативное примечание ниже раздела цитируемого проясняет это то, что происходит:

[Примечание: Когда конструктор шага не неявно объявленные или явно указан, выражения, которые иначе были бы вызвавшим Перемещение конструктора может вместо этого вызвать конструктор копирования. - конец примечание ]

+0

Я в надежде * это мести вниз. –

1

Как отмечалось, из §12.8

Если определение класса X не явно объявить конструктор перемещения, один будет неявно объявлен , как по умолчанию, если и только если

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

  • [...]

Обратите внимание на то, что объявлено пользователем. Но если вы посмотрите на §8.4.3:

Определение функции вида:

атрибут спецификатор-сл опт Децл-спецификатор-сл неавтоматического описатель вирт-спецификатор-сл opt = удалить;

называется удаленным определение. Функция с удаленным определением также называется удаленной функцией.

Программа, которая ссылается на удаленную функцию неявно или явно, кроме , объявляет ее, является плохо сформированной.

Таким образом, стандарт определяет delete d функции, как пользователь объявлено (как можно видеть из выше), даже если они являются delete д и не могут быть использованы.

Затем, согласно §12.8, конструктор неявного перемещения не определяется из-за объявленного пользователем (с = delete;) конструктора копирования.

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