Почему инициализация списка (с использованием фигурных скобок) лучше, чем альтернативы?

MyClass a1 {a};     // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);

Зачем?

Я не смог найти ответ на SO, поэтому позвольте мне ответить на мой собственный вопрос.

274

Решение

В основном копирование и вставка из Бьярна Страуструпа «Язык программирования C ++, 4-е издание»:

Инициализация списка не допускает сужения (§iso.8.5.4). То есть:

  • Целое число не может быть преобразовано в другое целое число, которое не может содержать его значение. Например, символ
    Допускается int, но не int to char.
  • Значение с плавающей точкой не может быть преобразовано в другой тип с плавающей точкой, который не может содержать его
    значение. Например, float to double допускается, но не double для float.
  • Значение с плавающей точкой не может быть преобразовано в целочисленный тип.
  • Целочисленное значение не может быть преобразовано в тип с плавающей точкой.

Пример:

void fun(double val, int val2) {

int x2 = val; // if val==7.9, x2 becomes 7 (bad)

char c2 = val2; // if val2==1025, c2 becomes 1 (bad)

int x3 {val}; // error: possible truncation (good)

char c3 {val2}; // error: possible narrowing (good)

char c4 {24}; // OK: 24 can be represented exactly as a char (good)

char c5 {264}; // error (assuming 8-bit chars): 264 cannot be
// represented as a char (good)

int x4 {2.0}; // error: no double to int value conversion (good)

}

только ситуация, когда = предпочтительнее, чем {}, при использовании auto ключевое слово, чтобы получить тип, определенный инициализатором.

Пример:

auto z1 {99}; // z1 is an initializer_list<int>
auto z2 = 99; // z2 is an int

Заключение

Предпочитайте {} инициализацию альтернативам, если у вас нет веских причин не делать этого.

249

Другие решения

Есть много причин использовать инициализацию скобок, но вы должны знать, что initializer_list<> Конструктор предпочтительнее других конструкторов, Исключением является конструктор по умолчанию. Это приводит к проблемам с конструкторами и шаблонами, где тип T Конструктор может быть либо списком инициализатора, либо простым старым ctor.

struct Foo {
Foo() {}

Foo(std::initializer_list<Foo>) {
std::cout << "initializer list" << std::endl;
}

Foo(const Foo&) {
std::cout << "copy ctor" << std::endl;
}
};

int main() {
Foo a;
Foo b(a); // copy ctor
Foo c{a}; // copy ctor (init. list element) + initializer list!!!
}

Предполагая, что вы не сталкиваетесь с такими классами, есть небольшая причина не использовать список инициализаторов.

77

Уже есть отличные ответы о преимуществах использования инициализации списка, однако мое личное правило — НЕ по возможности использовать фигурные скобки, а вместо этого сделать его зависимым от концептуального значения:

  • Если объект, который я создаю, концептуально содержит значения, которые я передаю в конструкторе (например, контейнеры, структуры POD, атомарные элементы, интеллектуальные указатели и т. Д.), То я использую фигурные скобки.
  • Если конструктор напоминает обычный вызов функции (он выполняет более или менее сложные операции, параметризованные аргументами), то я использую обычный синтаксис вызова функции.
  • Для инициализации по умолчанию я всегда использую фигурные скобки.
    Во-первых, таким образом, я всегда уверен, что объект инициализируется независимо от того, является ли он, например, является «реальным» классом с конструктором по умолчанию, который будет вызван в любом случае, или встроенным типом / POD. Второе — в большинстве случаев — соответствует первому правилу, поскольку инициализированный объект по умолчанию часто представляет собой «пустой» объект.

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

Это, например, прекрасно сочетается со стандартными типами библиотек, такими как std::vector:

vector<int> a{10,20};   //Curly braces -> fills the vector with the arguments

vector<int> b(10,20);   //Parentesis -> uses arguments to parameterize some functionality,
vector<int> c(it1,it2); //like filling the vector with 10 integers or copying a range.

vector<int> d{};      //empty braces -> default constructs vector, which is equivalent
//to a vector that is filled with zero elements
65
По вопросам рекламы [email protected]