MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Почему?
Я не мог найти ответ на SO, поэтому позвольте мне ответить на мой собственный вопрос.
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 int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99; // z3 is an int
Предпочитайте {} инициализацию альтернативам, если у вас нет веских причин не делать этого.
Уже есть отличные ответы о преимуществах использования инициализации списка, однако мое личное правило - НЕ использовать фигурные скобки всякий раз, когда это возможно, а вместо этого ставить его в зависимость от концептуального значения:
По моему опыту, этот набор правил может применяться гораздо более последовательно, чем использование фигурных скобок по умолчанию, но при этом необходимо явно помнить все исключения, когда они не могут быть использованы или имеют другое значение, чем "нормальный" синтаксис вызова функции с круглыми скобками. (вызывает другую перегрузку).
Это, например, прекрасно подходит для стандартных библиотечных типов, таких как std::vector
:
vector<int> a{10,20}; //Curly braces -> fills the vector with the arguments
vector<int> b(10,20); //Parenthesis -> uses arguments to parametrize 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
Есть много причин для использования инициализации скобок, но вы должны знать, что конструктор 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!!!
}
Предполагая, что вы не сталкиваетесь с такими классами, нет оснований не использовать список инициализаторов.
Это только безопаснее, если вы не строите с -не сужением, как, скажем, Google делает в Chromium. Если вы это сделаете, то это менее безопасно.
Примечание: А) Вьющиеся скобки безопаснее, потому что они не допускают сужения. B) Вьющиеся скобки менее безопасны, потому что они могут обходить частные или удаленные конструкторы и неявно вызывать явно помеченные конструкторы.
Эти две комбинации означают, что они безопаснее, если то, что находится внутри, является примитивными константами, но менее безопасны, если они являются объектами.