MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Зачем?
Я не смог найти ответ на SO, поэтому позвольте мне ответить на мой собственный вопрос.
В основном копирование и вставка из Бьярна Страуструпа «Язык программирования C ++, 4-е издание»:
Инициализация списка не допускает сужения (§iso.8.5.4). То есть:
Пример:
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
Предпочитайте {} инициализацию альтернативам, если у вас нет веских причин не делать этого.
Есть много причин использовать инициализацию скобок, но вы должны знать, что 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!!!
}
Предполагая, что вы не сталкиваетесь с такими классами, есть небольшая причина не использовать список инициализаторов.
Уже есть отличные ответы о преимуществах использования инициализации списка, однако мое личное правило — НЕ по возможности использовать фигурные скобки, а вместо этого сделать его зависимым от концептуального значения:
По моему опыту, этот набор правил может применяться гораздо более последовательно, чем использование фигурных скобок по умолчанию, но при этом необходимо явно помнить все исключения, когда они не могут быть использованы или имеют другое значение, чем «нормальный» синтаксис вызова функции с круглыми скобками. (вызывает другую перегрузку).
Это, например, прекрасно сочетается со стандартными типами библиотек, такими как 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