Удаленный конструктор по умолчанию. Объекты могут быть созданы... иногда
наивный, оптимистичный и ах.. так что неправильное представление о едином синтаксисе инициализации c++11
я думал, что начиная с C++11 пользовательские объекты типа должны быть построены новые {...}
синтаксис вместо старого (...)
синтаксис (за исключением перегруженного конструктора для std::initializer_list
и аналогичные параметры (например,std::vector
: размер ctor против 1 elem init_list ctor)).
преимущества: нет узких неявных преобразований, нет проблем с самым неприятным разбором, последовательность(?). Я не видел никаких проблем, поскольку я думал, что они одинаковы (за исключением приведенного примера).
но это не так.
Сказка о чистом безумии
на {}
вызывается конструктор по умолчанию.
... Кроме тех случаев, когда:
- конструктор по умолчанию удаляется и
- другие конструкторы не определены.
тогда похоже, что это скорее значение инициализирует объект?... Хотя объект удалил конструктор по умолчанию,{}
создать объект. Разве это не превосходит всю цель удаленного конструктора?
...Кроме тех случаев, когда:
- объект удаленный конструктор по умолчанию и
- другой конструктор(s) определены.
затем он терпит неудачу с call to deleted constructor
.
...Кроме тех случаев, когда:
- объект имеет удаленный конструктор и
- нет других конструктор определен и
- по крайней мере нестатический член данных.
затем он терпит неудачу с отсутствующими инициализаторами полей.
но тогда вы можете использовать {value}
построить объект.
Ok возможно, это то же самое, что и первое исключение (значение init объекта)
...Кроме тех случаев, когда:
- класс имеет удаленный конструктор
- и по крайней мере один член данных класса инициализации по умолчанию.
тогда, ни {}
, ни {value}
создать объект.
я уверен, что пропустил несколько. Ирония в том, что это называется униформа синтаксис инициализации. Я повторяю:униформа синтаксис инициализации.
что это за безумие?
Сценарий A
удалить конструктор по умолчанию:
struct foo {
foo() = delete;
};
// All bellow OK (no errors, no warnings)
foo f = foo{};
foo f = {};
foo f{}; // will use only this from now on.
Сценарий B
удалил конструктор по умолчанию, другие конструкторы удалены
struct foo {
foo() = delete;
foo(int) = delete;
};
foo f{}; // OK
Сценарий C
удалить конструктор по умолчанию, другие конструкторы, определенные
struct foo {
foo() = delete;
foo(int) {};
};
foo f{}; // error call to deleted constructor
Сценарий D
удален конструктор по умолчанию, другие конструкторы не определены, элемент данных
struct foo {
int a;
foo() = delete;
};
foo f{}; // error use of deleted function foo::foo()
foo f{3}; // OK
Сценарий E
удаленный конструктор по умолчанию, удаленный конструктор T, элемент данных t
struct foo {
int a;
foo() = delete;
foo(int) = delete;
};
foo f{}; // ERROR: missing initializer
foo f{3}; // OK
Сценарий F
удалены по умолчанию конструктор, инициализаторы членов данных в классе
struct foo {
int a = 3;
foo() = delete;
};
/* Fa */ foo f{}; // ERROR: use of deleted function `foo::foo()`
/* Fb */ foo f{3}; // ERROR: no matching function to call `foo::foo(init list)`
3 ответов
при таком взгляде на вещи легко сказать, что есть полный и полный хаос в том, как инициализируется объект.
большая разница происходит от типа foo
: если это агрегатный тип или нет.
это агрегате, если это:
- нет пользовательских конструкторов (удаленная или функция по умолчанию не считается предоставленной пользователем),
- не private или protected нестатических данных членов
- нет скобок или равных инициализаторов для нестатических элементов данных (начиная с c++11 до (возвращено) c++14)
- нет базовых классов,
- нет виртуальных функций-членов.
так:
- в сценариях A B D E:
foo
- это совокупность - в сценариях C:
foo
не является совокупностью - сценарий F:
- в C++11 это не объединять.
- в C++14-это совокупность.
- g++ не реализовал это и по-прежнему рассматривает его как неагрегат даже в C++14.
-
4.9
не выполнять этого. -
5.2.0
тут -
5.2.1 ubuntu
не делает (возможно, регрессия)
-
эффекты инициализации списка объекта типа T:
- ...
- если T является агрегатным типом, выполняется агрегатная инициализация. Это заботится о сценариях A B D E (и F в C++14)
- в противном случае конструкторы T рассматриваются в два этапа:
- все конструкторы, которые принимают std:: initializer_list ...
- в противном случае [...] все конструкторы T участвуют в разрешении перегрузки [...] Это заботится о C (и F в C++11)
- ...
:
Агрегатная инициализация объекта типа T (сценарии A B D E (F C++14)):
- каждый нестатический член класса, в порядке появления в определении класса, инициализируется копией из соответствующего предложения список инициализаторов. (ссылка на массив опущена)
TL; DR
все эти правила все еще могут показаться очень сложными и головная боль вызывает. Я лично слишком упрощаю это для себя (если я тем самым стреляю себе в ногу, то так тому и быть: я думаю, что проведу 2 дня в больнице, а не пару десятков дней головных болей):
- для агрегата каждый элемент данных инициализируется из элементов инициализатора списка
- еще называют конструктор
не бить все цели удалены конструктор?
Ну, я не знаю об этом, но решение сделать foo
не совокупность. Самая общая форма, которая не добавляет накладных расходов и не изменяет используемый синтаксис объекта, - это наследовать его от пустой структуры:
struct dummy_t {};
struct foo : dummy_t {
foo() = delete;
};
foo f{}; // ERROR call to deleted constructor
в некоторых ситуациях (нет нестатических членов на всех, я думаю), другой будет удалить деструктор (это сделает объект не материализуемых в любом контекст):
struct foo {
~foo() = delete;
};
foo f{}; // ERROR use of deleted function `foo::~foo()`
этот ответ использует информацию, собранную из:
большое спасибо @М. М кто помог исправить и улучшить этот пост.
что тебя беспокоит это агрегатной инициализации.
как вы говорите, есть преимущества и недостатки в использовании списка инициализации. (Термин "равномерная инициализация" не используется стандартом C++).
одним из недостатков является то, что список инициализации ведет себя по-разному для агрегатов, чем агрегаты. Кроме того, определение совокупность изменения немножко с каждым стандартом.
Сростки не создано с помощью конструктора. (Технически они действительно могут быть, но это хороший способ подумать об этом). Вместо этого при создании агрегата выделяется память, а затем каждый элемент инициализируется в соответствии с тем, что находится в инициализаторе списка.
для агрегатов создаются с помощью конструкторов, и в этом случае члены инициализатор списка аргументов конструктора.
на самом деле есть недостаток в дизайне выше: если у нас есть T t1; T t2{t1};
, затем намерение для выполнения копирования работ. Однако (до C++14) if T
является агрегатом, тогда вместо этого происходит агрегатная инициализация, и t2
' s первый член инициализируется с t1
.
этот недостаток был исправлен в отчет о неисправности который изменил C++14, поэтому отныне копировальная конструкция проверяется перед переходом к агрегатной инициализации.
определение совокупность из C++14:
агрегат-это массив или класс (пункт 9) без предоставленных Пользователем конструкторов (12.1), без частных или защищенных нестатических элементов данных (пункт 11), без базовых классов (пункт 10) и без виртуальных функций (10.3).
в C++11 значение по умолчанию для нестатического члена означало, что класс не является агрегатом; однако это было изменено для C++14. пользователем означает объявленный пользователем, но не = default
или = delete
.
если вы хотите убедитесь, что ваш конструктор вызывает никогда случайно выполняет агрегатную инициализацию, тогда вы должны использовать ( )
, а не { }
, и избегайте MVPs другими способами.
эти случаи вокруг агрегатной инициализации противоречат интуиции для большинства и были предметом предложения p1008: запретить агрегаты с объявленными пользователем конструкторами Он говорит:
C++ в настоящее время позволяет инициализировать некоторые типы с объявленными пользователем конструкторами через aggregate инициализация, минуя эти конструкторы. В результате код, удивление, замешательство, и детская коляска. В этом документе предлагается исправление, которое делает инициализацию семантика в C++ безопаснее, более однородна, и легче учить. Мы также обсуждаем изменения, которые это исправление вводит
и вводит некоторые примеры, которые хорошо перекрываются с делами, которые вы представляете:
struct X { X() = delete; }; int main() { X x1; // ill-formed - default c’tor is deleted X x2{}; // compiles! }
очевидно, что целью удаленного конструктора является предотвращение инициализации класса пользователем. Однако, вопреки интуиции, это не работает: пользователь все равно может инициализировать X через инициализацию aggregate, потому что это полностью обходит конструкторы. Автор может даже явно удалить все конструкторы default, copy и move и по-прежнему не предотвратить создание экземпляра X клиентским кодом через агрегатную инициализацию, как указано выше. Большинство разработчиков C++ удивлены текущее поведение при отображении этого кода Автор класса X может также рассмотреть возможность создания конструктора по умолчанию частный. Но если этот конструктор получает дефолтное определение, это снова не предотвращает aggregate инициализация (и, таким образом, создание экземпляра) класса:
struct X { private: X() = default; }; int main() { X x1; // ill-formed - default c’tor is private X x2{}; // compiles! }
из-за текущих правил агрегатная инициализация позволяет нам "по умолчанию строить" класс, даже если он не является, по сути, по умолчанию конструктивным:
static_assert(!std::is_default_constructible_v<X>);
пройдет для обоих определений X выше.
...
предлагаемые изменения:
изменить [dcl.в этом.aggr] пункт 1 следующего содержания:
An aggregate-это массив или класс (пункт 12) с
нет пользователей-при условии, явного, пользователей-заявил или по наследству конструкторы (15.1),не private или protected нестатических членов данных (п. 14),
нет виртуальных функций (13.3), и
нет виртуальной, частной или защищенной базы классы (13.1).
изменить [dcl.в этом.aggr] пункт 17 следующего содержания:
[Примечание: агрегатный массив или агрегатный класс могут содержать элементы класса >>типа с
пользователемпользователей-заявил конструктор (15.1). Инициализация > > этих агрегатных объектов описана в разделе 15.6.1. - конец Примечания]добавить в [разность.cpp17] в приложении C, раздел C. 5 C++ и ISO C++ 2017:
С. 5.6 п. 11: деклараторы [дифф.cpp17.dcl.decl]
пострадавших подпунктом: [dcl.в этом.примечанияа]
изменить: классе пользователей-заявил конструкторы-это не совокупность.
обоснование: удалить потенциально подверженная ошибкам агрегатная инициализация, которая может применяться не выдержав заявленного конструкторы класса.
влияние на первоначально функции: допустимый код C++ 2017, который aggregate-инициализирует тип с объявленным пользователем конструктором может быть неправильно сформирован или иметь различная семантика в этом международном стандарте.
затем примеры, которые я опускаю.
предложение принято и объединено в C++20 можно найти последний проект здесь, который содержит эти изменения, и мы можем см. изменения в [dcl.в этом.aggr]p1.1 и [dcl.в этом.примечанияа]Р17 и C++17 объявления diff.
Так это должно быть исправлено в C++20 вперед.