Когда при создании объекта правильно использовать (()), а когда {}?
Например, для std::vector,
std::vector<int> v1{1};
std::vector<int> v2((1));
конструкторы создадут разные векторы при внешней схожести вызова, v1 состоит из одного элемента со значением 1, а v2 из одного элемента со значением 0.
Вопрос, когда правильно использовать (()), а когда {} при создании любых объектов?
Ответы (1 шт):
Непонятно, зачем двойные скобки ((...)), когда одинарные (...) делают то же самое.
Никогда не используйте foo{...} со стандартными контейнерами. Используйте только foo = {...} или foo(...). Пустые фигурные скобки использовать можно: foo{}.
Дело в том, что смысл foo{...} меняется в зависимости от типа элементов:
std::vector<int> v1{10};= [10]std::vector<std::string> v1{10};= [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
А вот дальше есть две противоположные позиции:
Стараться нигде не использовать
foo{...}(пустые скобки все так же можно). По умолчанию использоватьfoo(...), а для инициализации контейнера списком:foo = {...}.Плюс: одинаковое правило для всех типов; не надо думать, где контейнер, а где нет.
Минус: нет защиты от сужающих преобразований, которую дают фигурные скобки.
Исключение: создание временного объекта явно безопасного типа, вроде
std::tupleилиstd::array(последний, хоть и контейнер, является агрегатом, т.е. не имеет никаких рукописных конструкторов, и поэтому безопасен).Почти везде использовать
foo{...}. Аfoo = {...}иfoo(...)только для контейнеров и для шаблонного кода (в который может прилететь тип-контейнер).Плюс: автоматическая проверка на сужающие преобразования.
Минус: надо следить, где контейнер (или где он может быть в шаблоне), а где нет; можно ошибиться.
Мне кажется (1) лучше. Если сужающие преобразования так беспокоят, то включать специальные варнинги против них и ловить их везде, а не только в конструкторах.
(1) считается более традиционным вариантом. (2) когда-то был новым модным вариантом, и если бы не прокол с контейнерами, был бы всем хорош.
В принципе, если отказаться от стандартных контейнеров и сделать свои, то их можно сделать так, чтобы обойти этот косяк: все конструкторы кроме одного с параметром initializer_list должны иметь какую-то дополнительную отметку, например принимать пустую структуру. Вот так:
struct with_size_t {};
inline constexpr with_size_t with_size;
my_vector<int> a{10}; // [10]
my_vector<int> a{with_size, 10}; // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]