Об инициализации вектора в C ++ с помощью {} - PullRequest
0 голосов
/ 25 апреля 2020

В книге Страуструпа "Программирование: принципы и практика программирования с использованием C ++ (второе издание)" автор создает три вектора, которые следуют в своей собственной структуре:

struct Day {
    vector<double> hour{ vector<double>(24,not_a_reading) };
};

struct Month { // a month of temperature readings
    int month{ not_a_month }; // [0:11] January is 0
    vector<Day> day{ 32 }; // [1:31] one vector of readings per day
};

struct Year { // a year of temperature readings, organized by month
    int year; // positive == A.D.
    vector<Month> month{ 12 }; // [0:11] January is 0
};

Мой вопрос: почему vector<Day> day{32}; создает вектор типа Day с размером 32, тогда как vector<int>{32}; создает вектор размером 1?

Ответы [ 3 ]

1 голос
/ 25 апреля 2020

Синтаксис type name{...}; (известный как единообразная инициализация ) был добавлен в C ++ 11 и должен был стать новым синтаксисом , заменяющим другие альтернативы. У него есть некоторые преимущества перед альтернативами, поэтому некоторые люди активно продвигают его.

Но есть проблема с ним: он ведет себя странно с некоторыми классами, которые не были написаны с учетом этого (в основном это стандартные контейнеры, которые были до того, как это было сделано).

В результате vector<T> name{...} сначала пытается использовать конструкторы с параметром std::initializer_list (который успешно выполняется для vector<int> name{42};, инициализируя вектор одним числом 42 ), и в случае неудачи он пытается использовать другие конструкторы (vector<Day> int{42}; заканчивается использованием конструктора с одним параметром size_t size, заполняя вектор 42 объектами, построенными по умолчанию).

Если вы думаете, что это поведение странное, вы не одиноки.

Практическое правило:

  • Избегайте синтаксиса type name{...} при работе с контейнерами (или полностью) .

  • Если вы хотите инициализировать контейнер со списком, используйте vector<T> name = {...};.
    (vector<Day> name = {42}; не скомпилируется.)

  • Если вы хотите использовать некоторые другой конструктор контейнера, используйте vector<T> name(...);.
    (vector<int> name(42); заполнит вектор 42 нулями.)

Обратите внимание, что vector<T> name(...); не будет работать в классе . Вместо этого вы можете использовать vector<T> name = vector<T>(...);.

1 голос
/ 25 апреля 2020

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

Таким образом, для этого объявления

std::vector<int> v{32};

используется ограничитель Класс std :: vector, имеющий первый параметр типа std::initializer_list<T>.

vector(initializer_list<T>, const Allocator& = Allocator());

В этом случае std::initializer_list<int> содержит только один элемент, равный 32.

В этом объявлении

std::vector<Day> day{32};

список инициализатора {32} не может быть преобразован в std::initializer_list<Day>. Поэтому конструктор, принимающий список инициализаторов, не подходит.

В этом случае компилятор рассматривает другие конструкторы и находит конструктор, который принимает целое число в качестве числа элементов в векторе.

explicit vector(size_type n, const Allocator& = Allocator());

Обратите внимание, что этот конструктор явный. Так, например, вы не можете написать

std::vector<Day> day = {32};

из стандарта C ++ 17 (13.3.1.7 Инициализация с помощью инициализации списка)

1 Когда объекты неагрегированного класса имеют тип T инициализируются списком так, что 8.5.4 указывает, что разрешение перегрузки выполняется в соответствии с правилами этого раздела, разрешение перегрузки выбирает конструктор в два этапа :

(1.1) - Первоначально функции-кандидаты являются конструкторами списка инициализаторов (8.5.4) класса T , а список аргументов состоит из списка инициализаторов как единственного аргумента.

(1.2) - Если жизнеспособный конструктор списка инициализаторов не найден , разрешение перегрузки выполняется снова, где все функции-кандидаты являются конструкторами класса T, а список аргументов состоит из элементов списка инициализатора.

0 голосов
/ 25 апреля 2020

std::vector имеет конструкторов заполнения и инициализации списка, которые нам подходят.

Day не может быть инициализировано значением 32, поэтому вызывается конструктор заполнения.

Это можно легко проверить, добавив соответствующий конструктор Дня:

struct Day {
    Day(int) {}
    vector<double> hour{ vector<double>(24,not_a_reading) };
};

struct Month { // a month of temperature readings
    int month{ not_a_month }; // [0:11] January is 0
    vector<Day> day{ 32 }; // [1:31] one vector of readings per day
};

struct Year { // a year of temperature readings, organized by month
    int year; // positive == A.D.
    vector<Month> month{ 12 }; // [0:11] January is 0
};

// vector<Day> day{32};
// day.size() == 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...