Подтвердить что ты не робот

Каковы различия между C-like, конструктором и равномерной инициализацией?

Насколько мне известно, есть три способа инициализации переменной в C++.

int x = 0;    // C-like initialization
int x (0);    // Constructor initialization
int x {0};    // Uniform initialization

Равномерная инициализация была введена для C++ 11, чтобы обеспечить более равномерный синтаксис для инициализации различных типов переменных, что требовало другого синтаксиса в C++ 03.

Каковы различия между C-like, конструктором и равномерной инициализацией? И я должен всегда использовать равномерную инициализацию?

4b9b3361

Ответ 1

Во-первых, я бы рекомендовал посмотреть следующие беседы Herb Sutter, в котором он дает некоторые рекомендации по этому вопросу. Обсуждение инициализации скобки начинается с около 23:00.

Когда вы говорите о примитивных типах данных, все 3 дают одинаковый результат. Я лично предпочитаю придерживаться старого синтаксиса int x = 0, но это сводится к личным предпочтениям.

Для типов классов, инициализация скобок и инициализация конструктора старой школы не являются полностью взаимозаменяемыми. Например:

vector<int> v (100); // Creates a 100-element vector
vector<int> v {100}; // Creates a 1-element vector, holding the value 100.

Это связано с тем, что std::vector имеет конструктор, который явно определяет std::initializer_list как единственный аргумент. Имейте в виду, что

auto var = {1, 2};

создает std::initializer_list с var в качестве своего идентификатора.

Вещь списков инициализаторов заключается в том, что они обеспечивают согласованность, которая является долгожданным изменением от того, что было доступно заранее. Например, если вы хотите инициализировать массив на С++, вы должны использовать:

int arr[] = {1, 2, 3, 4};

Но если вы хотите инициализировать vector<int> с теми же элементами, вам либо пришлось:

  • Сначала инициализируйте вышеуказанный arr, а затем передайте arr и arr + 4
  • Создайте вектор и push_back() элементы по отдельности или в цикле.

С С++ 11 вы можете просто использовать

vector<int> v = {1, 2, 3, 4}; // Same syntax. Nice! Note that the = is optional

Другим примером, в котором рекомендуется инициализация скобок, является то, что он обеспечивает обходное решение для С++ наиболее неприятного анализа. Из разговора предположим, что у нас есть два класса: origin и extents, экземпляры которых могут быть переданы для построения другого объекта типа rectangle. Следующее утверждение:

rectangle w(origin(), extents());

не позволяет вам создать объект rectangle, используя origin и extents temporaries, потому что этот оператор анализируется как объявление функции. Tsk tsk. Так что обычно вам нужно будет:

origin  o;
extents e;
rectangle w(o, e);

С инициализацией скобки вы можете создать их на лету, а

rectangle w {origin(), extents()};

будет работать по назначению, т.е. передается конструктору, который перегружен объектом origin в качестве первого аргумента, а объект extents - вторым.

Правило для объектов, используйте фиксацию скобки, если у вас нет причины.

Ответ 2

Каковы различия между c-like, конструктором и равномерной инициализацией?

Для примитивных типов, таких как int, нет никакой практической разницы; поэтому рассмотрим тип класса T.

Первый стиль эквивалентен

T x(T(0));

создание временного объекта из выражения инициализатора, а затем инициализация x путем перемещения или копирования этого. На практике перемещение или копия будут отменены, так что результат будет таким же, как и второй стиль; единственное отличие состоит в том, что первый будет терпеть неудачу, если нет доступного конструктора копирования или перемещения.

Второй непосредственно инициализирует объект, используя конструктор, который принимает один аргумент, выдавая ошибку, если нет подходящего конструктора.

Третий зависит от того, какие конструкторы доступны.

  • если конструктор принимает std::initializer_list, он использует это:
  • в противном случае, если конструктор принимает один аргумент подходящего типа, он использует это:
  • в противном случае, если он является агрегатом (без конструкторов) с одним элементом, этот элемент инициализируется нулем;
  • В противном случае это ошибка.

И должен ли я всегда использовать равномерную инициализацию?

Нет. Иногда вам нужна инициализация в стиле функции, чтобы различать конструктор initializer_list, а другой - другие типы аргументов. Например:

std::vector<int> v1(10, 42);  // 10 elements with value 42
std::vector<int> v2{10, 42};  // 2 elements with values 10 and 42

Вы также не должны называть его "равномерной инициализацией", поскольку он не является "единообразным" в каком-либо значимом смысле. Официальным термином является "скобка-инициализация".