Ответ 1
Нет, это не то же самое.
Разница между ними та же, что применяется для прямой инициализации и для инициализации копирования, которая является тонкой, но часто очень запутанной.
§12.6.2 [class.base.init]:
Список-список выражений или бит-init-list в mem-initializer используется для инициализации указанного подобъекта (или, в случае делегирующего конструктора, полного объекта класса) в соответствии с правилами инициализации 8.5 для прямой инициализации. [...]
В конструкторе без делегирования, если данный нестатический член данных или базовый класс не обозначен идентификатором mem-initializer (включая случай, когда нет списка mem-initializer, поскольку конструктор не имеет ctor-initializer), и сущность не является виртуальным базовым классом абстрактного класса (10.4), то
- если объект является нестатическим членом данных, у которого есть инициализатор скобок или равный, объект инициализируется , как указано в 8.5;
§8.5 [dcl.init]:
Инициализация, которая встречается в форме
T x = a;
а также при передаче аргументов, возврату функции, выделении исключения (15.1), обработке исключения (15.3) и инициализации элемента агрегации (8.5.1) называется копирование-инициализация.
Инициализация нестатического элемента данных в списке-initializer-list соответствует правилам прямой инициализации, которые не создают промежуточные временные файлы, которые необходимо переместить/скопировать (если они скомпилированы без копирования-элиты), ни тип элемента данных должен быть с возможностью копирования/перемещения (даже если копия завершена). Кроме того, прямая инициализация вводит явный контекст, в то время как инициализация копирования не явная (если конструктор, выбранный для инициализации, равен explicit
, программа не будет компилироваться).
Другими словами, синтаксис obj s = obj("value");
не будет компилироваться, если obj
объявлен как:
struct obj
{
obj(std::string) {}
obj(const obj&) = delete;
};
или
struct obj
{
obj(std::string) {}
explicit obj(const obj&) {}
};
В качестве более ощутимого примера, в то время как ниже не будет компилироваться:
struct any
{
std::atomic<int> a = std::atomic<int>(1); // ill-formed: non-copyable/non-movable
std::atomic<int> b = 2; // ill-formed: explicit constructor selected
};
это будет:
struct any
{
std::atomic<int> a;
std::atomic<int> b{ 2 };
any() : a(1) {}
};
Какой способ лучше (производительность)?
С включенным копированием, обе имеют идентичную производительность. Если отключено копирование, есть дополнительный вызов конструктора копирования/перемещения при каждом экземпляре, когда используется синтаксис копирования-инициализации (который obj s = obj("value");
является одним из).
Есть ли другой способ?
Синтаксис подстановки или выравнивания-инициализатора также позволяет выполнить инициализацию прямого списка:
class any {
public:
obj s{ "value" };
any() {}
};
Есть ли другие отличия?
Некоторые другие отличия, которые стоит упомянуть, следующие:
- Элемент-бит-или-равный-инициализатор должен находиться в файле заголовка вместе с объявлением класса.
- Если оба они объединены, список-инициализатор-член имеет приоритет по сравнению с принципом привязки-равным-инициализатором (то есть, игнорируется элемент выравнивания с равным или равным).
- (только для С++ 11, до С++ 14) Класс, который использует механизм скрепления или равный-инициализатор, нарушает ограничения для агрегатного типа.
- С синтаксисом brace-or-equal-initializer невозможно выполнить прямую инициализацию, отличную от инициализации прямого списка.