Почему наличие деструктора делает класс не копируемым? - PullRequest
1 голос
/ 01 февраля 2020

У меня есть эта простая программа, которая не компилирует

#include <future>

class Foo
{
public:
    ~Foo();
    std::future<int> f;
};

Foo getFoo()
{
    return Foo();
}

int main()
{
    Foo b = getFoo();
}

, что, я думаю, имеет смысл. Foo не копируется, потому что future не копируется.

 In function 'Foo getFoo()':
13:16: error: use of deleted function 'Foo::Foo(const Foo&)'
4:7: note: 'Foo::Foo(const Foo&)' is implicitly deleted because the default definition would be ill-formed:
4:7: error: use of deleted function 'std::future<_Res>::future(const std::future<_Res>&) [with _Res = int]'
In file included from 2:0:
/usr/include/c++/4.9/future:686:7: note: declared here
       future(const future&) = delete;
       ^
 In function 'int main()':
18:20: error: use of deleted function 'Foo::Foo(const Foo&)'

Я не понимаю, почему он компилируется при удалении деструктора:

#include <future>

class Foo
{
public:
    //~Foo();
    std::future<int> f;
};

Foo getFoo()
{
    return Foo();
}

int main()
{
    Foo b = getFoo();
}

Isn ' этот деструктор генерируется по умолчанию? Как это влияет на то, что класс можно копировать / перемещать?

Ответы [ 2 ]

3 голосов
/ 01 февраля 2020

Неявно сгенерированный конструктор копирования определен как удаленный , поскольку ваш класс содержит std::future, который нельзя скопировать. Это означает, что ваш класс не может быть скопирован в любом случае.

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

Как упомянуто @Ayxan в комментарии к вопросу, поскольку C ++ 17 скомпилирует оба ваших примера, поскольку, хотя Foo не может быть перемещен в первом примере, поскольку C ++ Применяются 17 обязательных правил исключения, и в вашем примере будет только один Foo, встроенный непосредственно в Foo b. Временных Foo объектов не будет, и для этого не нужно будет копировать или перемещать класс.


В общем случае вы всегда должны реализовывать конструктор копирования / перемещения и операторы присваивания вручную, если у вас есть пользовательский деструктор. Это известно как правило 3/5 . Язык в настоящее время не применяет это правило для операций копирования, хотя неявное объявление операций копирования, если существует объявленный пользователем деструктор, устарело с C ++ 11. Тем не менее, язык обеспечивает соблюдение правила для операций перемещения, неявно объявляя операции перемещения, если присутствует объявленный пользователем деструктор.

Пользовательский деструктор не требуется часто, поэтому вам, вероятно, не следует объявлять его в первое место. Если вам нужно объявить это, потому что, например, вам нужно, чтобы оно было virtual (в этом случае оно должно быть просто по умолчанию с virtual ~Foo() = default), тогда вы можете по умолчанию операции перемещения:

Foo(Foo&&) = default;
Foo& operator=(Foo&&) = default;

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

1 голос
/ 01 февраля 2020

В c ++ 11 неявное определение конструктора копирования по умолчанию считается устаревшим, если в классе есть пользовательский оператор копирования или пользовательский деструктор.

Это стандарт языка.

Обратите внимание, что у вас есть некопируемый член.

...