Подтвердить что ты не робот

Зачем нам нужно использовать виртуальную ~ A() = default; вместо виртуального ~ A() {} в С++ 11?

В Qaru post Проверка типа объекта в С++ 11, у меня есть комментарий:

В С++ 11 вы действительно захотите сделать virtual ~A() = default; В противном случае вы потеряете конструкторы перемещения implict.

Что такое virtual ~A() = default; для? Как происходит неявное перемещение конструкторов, потерянных с помощью virtual ~A() {}?

4b9b3361

Ответ 1

Комментарий неверен.

И

virtual ~A() = default;

и

virtual ~A() {}

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

[dcl.fct.def.default]/p4 обсуждает объявленные пользователем и специальные пользовательские элементы:

Специальная функция-член предоставляется пользователю, если она объявлена ​​пользователем и явно не дефолт или не удаляется по его первой декларации.

Ответ 2

В этом сообщении fooobar.com/questions/110494/... у меня есть комментарий:

В С++ 11 вы действительно захотите сделать virtual ~A() = default; В противном случае вы потеряете конструкторы перемещения implict.

Комментарий неверен.

Даже default ed, деструктор "объявлен пользователем" (хотя обратите внимание, что он также не "предоставляется пользователем" ).

#include <iostream>

struct Helper
{
    Helper() {}
    Helper(const Helper& src) { std::cout << "copy\n"; }
    Helper(Helper&& src)      { std::cout << "move\n"; }
};

struct A
{
    virtual ~A() {}
    Helper h;
};

struct B
{
    virtual ~B() = default;
    Helper h;
};

struct C
{
    Helper h;
};


int main()
{
    {
        A x;
        A y(std::move(x));   // outputs "copy", because no move possible
    }

    {
        B x;
        B y(std::move(x));   // outputs "copy", because still no move possible
    }

    {
        C x;
        C y(std::move(x));   // outputs "move", because no user-declared dtor
    } 
}

Живая демонстрация:

+ g++ - 4.8 -std = С++ 11 -O2 -Wall -pthread main.cpp
+./a.out
копия
копия
переместить

Итак, вы не "потеряли" что-нибудь; здесь не было никаких функций перемещения.

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

[C++11: 12.8/9]: Если определение класса X явно не объявляет конструктор перемещения, он будет объявлен неявным образом как дефолт, если и только если

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

Bootnote

Не помешало бы, если бы в будущей версии стандарта фактически были указаны точные значения таких терминов, как "объявленный пользователем". По крайней мере, это:

[C++11: 8.4.2/4]: [..] Специальная функция-член предоставляется пользователю, если она объявлена ​​пользователем и явно не дефолтна или удалена в ее первом объявлении. [..]

Здесь можно различать это различие.

Ответ 3

Этот комментарий неверен.

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

~A() { … }

но и этот

~A() = default;

заставляет компилятор не предоставлять неявный деструктор. Во-первых, это определение и, следовательно, декларация; вторая - это просто декларация. В обоих случаях деструктор объявляется пользователем и, таким образом, запрещает компилятору предоставлять неявный конструктор перемещения.

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