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

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

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

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

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

41

Решение

Во-первых, я бы порекомендовал посмотреть на следующий разговор Херб Саттер, в котором он дает некоторые рекомендации по этому вопросу. Обсуждение инициализации фигурной скобки начинается в около 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 как его идентификатор.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

47

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

Каковы различия между 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

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

13

По вопросам рекламы [email protected]