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

Последние несколько дней я играл с C ++ 11 и придумал что-то странное.

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

int a{5};

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

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

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

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

Но не как объявление b - это кажется непоследовательным. Я видел некоторые другие вещи с таким же эффектом. Что я спрашиваю - это поведение в окончательном стандарте C ++ 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

Ответы [ 3 ]

22 голосов
/ 15 марта 2012

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

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

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

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

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

6 голосов
/ 15 марта 2012

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

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

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

2 голосов
/ 15 марта 2012

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

...