Какой смысл в дефолтных функциях в С++ 11?

С++ 11 добавляет способность сообщать компилятору создать стандартную реализацию любого из специальные функции-члены. Хотя я могу видеть значение удаления функции, где значение явно дефолт функции? Просто оставьте поле пустым, и компилятор все равно сделает это.

Единственное, что я вижу, это то, что конструктор по умолчанию создается только тогда, когда не существует другого конструктора:

class eg {
public:
    eg(int i);
    eg() = default; 
};

Но действительно ли это лучше, чем вы это делаете сейчас?

class eg {
public:
    eg(int i);
    eg() {}
};

Или мне не хватает прецедента?

Ответ 1

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

Ответ 2

Те примеры из сайта Stroustrup могут помочь вам понять суть:

дефолтные и удаленные функции - управление настройками по умолчанию

Общая идиома "запрет копирование" теперь можно выразить непосредственно:

class X {
  // ...

  X& operator=(const X&) = delete;    // Disallow copying
  X(const X&) = delete;
};

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

class Y {
  // ...
  Y& operator=(const Y&) = default;   // default copy semantics
  Y(const Y&) = default;

};

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

struct Z {
  // ...

  Z(long long);     // can initialize with an long long
  Z(long) = delete; // but not anything less
};

Ответ 3

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

struct S
{
    virtual ~S();
    virtual S& operator=(const S&);
};

S::~S() = default;
S& S::operator=(const S&) = default;

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

  • доступ (будет сделан непубличным)
  • виртуальный
  • Явный (конструкторы)
  • спецификации исключения
  • константа параметров

но для этого функции должны быть определены вне класса (8.4.2/2 в С++ 0x Final Committee Draft).

Версия исходного предложения Лоуренса Кроула здесь.

Благодаря Роджер Пате для разъяснения и цитирования.

Ответ 4

1) Неявно сгенерированные деструкторы в настоящее время не являются виртуальными. Поэтому вам нужно определить их, чтобы сделать их виртуальными, и в этом случае они не так эффективны. With = default, у вас будут как виртуальные, так и эффективные, как неявно созданные деструкторы.

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

3) Если вы встроили свой конструктор по умолчанию, ваш класс по-прежнему остается тривиальным.

Вот статья, посвященная этой новой функции.

Ответ 5

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

Ответ 6

См. статью 17 от большой книги Скотта Майера " Эффективный современный С++". В нем описаны многие условия, при которых генерируются конструкторы копирования по умолчанию, операции копирования и операции перемещения (или НЕ сгенерированы).

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

Из вещей, которые нужно запомнить в конце пункта 17:

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

  • Конструктор копирования генерируется только для классов, не имеющих явно объявленного конструктора копирования, и его удаляется, если объявлена ​​операция перемещения. Оператор присваивания копий генерируется только для классов, не имеющих явно объявленного оператора назначения копирования, и его удаляется, если объявлена ​​операция перемещения. Генерация операций копирования в классах с явно объявленным деструктором устарела.

Ответ 7

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

Ответ 8

Defaulting более полезен для copy-constructors, если у вас есть класс с большим количеством атрибутов. Например, если у вас есть этот класс:

class MyClass {
private:
   int offset;
   std::string name;
   std::vector<Person*> relatives;
   float weight;
   MyClass* spouse;
   Vehicle* car;
   double houseArea;
   Date birth;
   Person* parents[2];

public:
   /* Default constructor will be defined here */
};

вместо определения конструктора-копии следующим образом:

MyClass(const MyClass& that) :
   offset(that.offset),
   name(that.name),
   relatives(that.relatives),
   weight(that.weight),
   spouse(that.spouse),
   car(that.car),
   houseArea(that.houseArea),
   birth(that.birth),
   parents(that.parents)
{}

вы бы определили этот способ:

MyClass(const MyClass&) = default;