зачем явно удалять конструктор?

95

Когда / зачем мне явно удалять конструктор? Если предположить, что причина в том, чтобы предотвратить его использование, почему бы просто не сделать это private?

class Foo
{ 
  public: 
    Foo() = delete; 
};
ясень
источник
14
Это вроде как хорошо сочетается = default, даже класс не может его использовать, и я лично предпочитаю видеть использование удаленной функции. над Функция является частной. В первом прямо говорится: «Это не предназначено для использования». Если из этого что-то выходит, класс, неспособный использовать это, фактически имеет семантическую разницу.
Крис
16
Честно говоря, я думаю, что люди начинают агрессию с закрытыми голосами. Я не понимаю, насколько это неконструктивно.
Лучиан Григоре 01
4
@LuchianGrigore: Согласен. Мне было интересно, почему сообщество стало таким жестким. Я не вижу в этом смысла.
Эд С.
11
Поскольку я редко использую C ++ 11, это для меня более информативно, чем, вероятно, даже понимает OP. Я даже не знал, что вы можете пометить конструктор для delete. И вопрос, и ответ Лучиана легко квалифицировать как конструктивные. Любой, кто не вдыхает тонкости C ++ 11, но скоро должен будет получить кое-что из обоих.
WhozCraig 01

Ответы:

89

Как насчет:

//deleted constructor
class Foo
{ 
  public: 
    Foo() = delete;     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //illegal
}

против

//private constructor
class Foo
{ 
  private: 
    Foo() {}     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //legal
}

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

Образец здесь .

Лучиан Григоре
источник
3
Вам вообще не нужно объявлять Foo (), если вы объявляете Foo (int). Foo () не будет сгенерирован, поэтому Foo f в любом случае недействителен. Таким образом, ваш пример не показывает случай удаленного конструктора. Убедитесь
отметьте
1
@mark Я написал 2 конструктора, чтобы доказать это. Отредактирую, чтобы всем было понятно.
Лучиан Григоре 01
1
Я понимаю разницу, я просто не понимаю добавленную стоимость оператора удаления в целом и для конструктора в частности. В конце концов, я мог бы указать частный конструктор по умолчанию без тела. Тогда и код тоже выходит из строя, только при линковке. Что ж, я вижу, что удаление более явно передает намерение, но это все.
отметьте
11
@mark Да, это был бы способ работы C ++ 98. Но ИМХО, четкая передача намерения на самом деле очень важная вещь в программировании в целом. В этом случае некоторые читатели могут увидеть частный неопределенный конструктор и предположить, что он случайный, и просто добавить для него определение, особенно если определение столь же тривиально, как и конструктор по умолчанию (да, наличие комментария помогает, но мы бы предпочли компилятор -принуждение к принуждению к комментарию). Имея более четкое представление о намерениях, мы также получаем гораздо лучшее сообщение об ошибке, в котором говорится «явно удалено», а не «неопределенная ссылка».
mpark 05
2
Честно говоря, не понимаю, как это отвечает на главный вопрос. Вопрос в заголовке и первый вопрос OP в сообщении был: когда и почему я должен явно удалить свой конструктор?
Александр Болинский
13

зачем явно удалять конструктор?

Другая причина:
я использую, deleteкогда хочу убедиться, что класс вызывается с инициализатором. Я считаю это очень элегантным способом добиться этого без проверок во время выполнения.

Компилятор C ++ выполнит эту проверку за вас.

class Foo
{
   public:
       Foo() = delete;
       Foo(int bar) : m_bar(bar) {};
   private:
       int m_bar;
}

Этот - очень упрощенный - код гарантирует, что такого экземпляра не существует:Foo foo;

Питер ВАРГА
источник
12
Удаленная декларация здесь не нужна. Он автоматически удаляется любым конструктором, предоставленным пользователем
Майк Луи
5
Чтобы прояснить комментарий @MikeLui, удаленное объявление не нужно компилятору . Существует множество случаев, когда подобный код следует включать, чтобы заявить о своем намерении другим программистам .
Джефф Джи
Наряду с объявлением вашего намерения, он создает очевидное место для документирования вашей причины его удаления в общедоступном интерфейсе, и, кроме того, ошибка компилятора будет чем-то коротким, например «использование удаленной функции». Если бы Fooбыло несколько конструкторов, а не конструкторов по умолчанию, Foo foo;это вызвало бы гораздо более длинную ошибку, в которой перечислялись бы все неявно определенные, защищенные и частные конструкторы, которым не удалось сопоставить.
сигма
Я до сих пор не понимаю, как дополнительная строка с объявлением конструктора, в котором ключевое слово "= delete" декларирует намерение "без конструктора по умолчанию" лучше, чем ... просто без конструктора по умолчанию? Пример: я не хочу объявлять переменную «a» в моем коде - что лучше, написать «// int a; // не нужно определять переменную a» или просто ничего не писать об этой переменной в коде?
Еж
2

Я встречал ctors по умолчанию, объявленные как «удаленные» в исходном коде LLVM (например, в AlignOf.h). Связанные шаблоны классов обычно находятся в специальном пространстве имен, называемом 'llvm :: detail'. Я думаю, вся цель заключалась в том, что они рассматривали этот класс только как вспомогательный. Они никогда не собирались создавать их экземпляры; только для использования их в контексте других шаблонов классов с некоторыми уловками метапрограммирования, которые выполняются во время компиляции.

Например. есть этот шаблон класса AlignmentCalcImpl, который используется только в другом шаблоне класса с именем AlignOf в качестве параметра для оператора sizeof (.). Это выражение можно вычислить во время компиляции; и нет необходимости создавать экземпляр шаблона -> так почему бы не объявить удаление ctor по умолчанию, чтобы выразить это намерение.

Но это только мое предположение.

Гибаци
источник