2012-03-15 4 views
22

Я играл с C++ 11 в течение последних нескольких дней, и я придумал что-то странное.Почему 'std :: vector <int> b {2};' создать 1-элементный вектор, а не 2-элементный?

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

int a{5}; 

Но если бы я сделать то же самое в станд :: вектор:

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

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

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

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

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

Edit: этот код:

#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

+1

@ildjarn: Я могу подтвердить это на gcc, и поскольку он имеет список инициализаторов ctor, кажется, что это правильно. – PlasmaHH

+0

Обратите внимание, что (ложное) обещание равномерной инициализации не означает, что вы можете повсюду заменить() на {}, только что вы теперь можете использовать {} в гораздо большем количестве мест. – PlasmaHH

+1

Я не эксперт C++ 11, но я просто взял на него учебный класс, и это выглядит правильно для меня. – Almo

ответ

21

Да, такое поведение предназначено, в соответствии с §13.3.1.7 инициализацией списка инициализации

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

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

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

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

+1

ОК. Спасибо за разъяснения. Почему комитет по стандартам просто не потребовал, чтобы люди пишут std :: vector a ({2}) ;? Таким образом, нет необходимости компилятора внедрять новый синтаксис инициализации и применять его к примитивам. Если цель состоит в том, чтобы позволить людям использовать {} в большем количестве мест, то это похоже на антифезию. Раньше() и {} у каждого было свое место. Но теперь {} можно использовать всегда, кроме случаев, когда это невозможно. Поэтому моя реакция на кишок состоит в том, чтобы полностью исключить новую функцию и использовать старый синтаксис, чтобы избежать раздражающих ошибок. –

+0

Хорошо. Просто видел ваше редактирование. Просто прискорбно, что каждый продает инициализацию списка как все, и заканчивает все инициализацию для C++ и рекламирует его как огромную функцию. Я не обязательно считаю, что это самое интуитивное или последовательное поведение, но есть причина, по которой я не могу написать стандарт: P –

+0

@RobertMason: Это огромная функция, но никак не «все и заканчивать все ». Я недовольна тем, что так думают люди, потому что это неверно. –

2

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

+0

Да, это поддерживается C++ 11 §13.3.1.7/1. – ildjarn

+1

Да, но для меня это эквивалентно принятию std :: vector a (2), а затем говорят, что std :: vector a2 делает то же самое. Мне это кажется не очень последовательным. И весь смысл равномерной инициализации заключается в обеспечении большей согласованности AFAIK. Так зачем добавить это? –

+1

@ Robert Mason, я думаю, что здесь можно было бы прочитать «форму», поскольку вы можете инициализировать std :: vector, std :: array, c-style array, std :: set, std :: list и определенные пользователем типы коллекций в так же. Я не говорю, что это здорово, мне потребовалось некоторое время, чтобы окунуться в него. – juanchopanza

5

Унифицированная инициализация не означает, что вы думаете, что она делает. Он был добавлен, чтобы сделать инициализацию более однородной среди типов на 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; 
} 

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

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

+3

"* Это не значит использовать' {} 'вместо'() 'для конструкторов, это было бы глупо. * На самом деле, это именно то, что означает, что нет перегрузок конструктора, которые принимают только' std: : initializer_list <> 'instance. – ildjarn

+2

Stroustrup упоминает в своем FAQ http://www2.research.att.com/~bs/C++0xFAQ.html#uniform-init, что для обычной инициализации используется полная инициализация. Но есть исключения (например, 'std :: initializer_list's), где вы должны использовать« старый стиль ». – evnu

+0

@ildjarn: ИМО это не должно. Если кто-то может оправдать меня, почему он должен называть других конструкторов, [я готов слушать] (http://chat.stackoverflow.com/rooms/10/loungec). В то же время, я изменил эту фразу на что-то менее спорным. –

 Смежные вопросы

  • Нет связанных вопросов^_^