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

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

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

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

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

2 ответа

Решение

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

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

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

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

Другие вопросы по тегам