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

Почему "std::vector <int> b {2}; ' создать 1-элементный вектор, а не 2-элементный?

Я играл с С++ 11 за последние несколько дней, и я придумал что-то странное.

Если я хочу равномерно инициализировать int:

int a{5};

Но если я сделаю то же самое с std::vector:

std::vector<int> b{2};

Не создает массив из двух элементов, а массив с одним элементом значения два. Похоже, чтобы получить этот эффект, нужно было бы более подробно говорить об этом:

std::vector<int> c{{2}};
std::vector<int> d = {2};

Но не так, как объявление b - это кажется непоследовательным. Я видел некоторые другие вещи с тем же эффектом. То, о чем я прошу, - это такое поведение в последнем стандарте С++ 11, или это только в проекте, который был реализован раньше? Если да, то почему комитет по стандартам включил это поведение? Похоже, что он побеждает всю цель равномерной инициализации, так как нужно помнить, какие классы имеют конструкторы списка инициализаторов, и использовать синтаксис old() вместо {} только с этими классами. Или вообще не выполняется равномерная инициализация.

Это похоже на большую "добычу". Но может быть и преимуществ, о которых я не знаю.

Изменить: этот код:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> a{2};
    for (auto x: a) {
        std::cout << x << std::endl;
    }
    return 0;
}

выводит "2" на gcc 4.6.2

4b9b3361

Ответ 1

Да, это поведение предназначено, согласно §13.3.1.7 Инициализация с помощью инициализации списка

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

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

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

Что касается "всей цели единой инциализации"... "Единая инициализация" - это маркетинговый термин, а не очень хорошее описание. Стандарт имеет все обычные формы инициализации плюс инициализацию списка, но нет "равномерной инициализации". Инициализация списка не предназначена для конечной формы инициализации, это просто еще один инструмент в поясе полезности.

Ответ 2

Унифицированная инициализация не означает, что вы думаете, что она делает. Он был добавлен, чтобы сделать инициализацию более однородной среди типов на С++. Обоснование таково:

typedef struct dog_ {
   float height;
   int weight;
} dog;
int main() { 
    dog Spot = { 25.6, 45};
    dog Data[3] = { Spot, {6.5, 7} };
    std::array<dog, 2> data = { { Spot, {6.5, 7} } }; //only in C++ obviously
    return 0;
}

Это действительный код C и С++ и существует уже много лет. Это было действительно удобно, но вы должны были помнить, что это работало только с типами POD. Люди долго жаловались, что нет способа сделать std::vector<int> data = { 3, 7, 4, 1, 8};, но некоторые классы (std::array) были написаны странными способами, чтобы разрешить конструкторы списка инициализаторов.

Итак, для С++ 11 комитет сделал это так, чтобы мы могли сделать этот вектор и другие классные классы. Это сделало конструкцию всех типов более однородной, чтобы мы могли использовать {} для инициализации через конструкторы, а также из списков значений. Проблема, с которой вы сталкиваетесь, заключается в том, что перегрузка конструктора с помощью std::initializer_list<int> является наилучшим совпадением и будет выбрана в первую очередь. Таким образом, std::vector<int> b{2}; не означает вызов конструктора, который принимает int, вместо этого он означает создание vector из этого списка значений int. В этом свете совершенно ясно, что он создал бы vector, содержащее единственное значение 2. Чтобы вызвать другой конструктор, вам придется использовать синтаксис (), поэтому С++ знает, что вы не хотите инициализировать его из списка.

Ответ 3

В стандарте указано, что конструктор списка инициализатора имеет приоритет над другими. Это всего лишь один случай, когда это невозможно, просто замените () на {}. Есть и другие, например {} инициализация не допускает сужения конверсий.