2011-08-11 2 views
17

Как и многие, я очень взволнован C++0x. Я стараюсь учиться и использовать новые функции в новых проектах, поэтому я могу написать лучший, самый простой в обслуживании код.C++ 0x равномерная инициализация «oddity»

Излишне говорить, что мне нравится идея новых инициализаторов. Так что я смотрю на них, и они имеют смысл для меня:

T x = { 1, 2, 3 }; // like a struct or native array 
T x({1, 2, 3}); // copy construct something like an "object literal" in other languages... cool! 
return {1, 2, 3}; // similar to above, but returning it, even cooler! 

Что не имеет смысла для меня это:

T x{1, 2, 3}; 

Он просто чувствует ... странно. Я не уверен, какой синтаксис, который люди хотят использовать, это подражание, это просто не кажется «правильным».

Что за дизайн/мысль за этим синтаксисом?

Единственный пример, когда кажется, что он делает различие что-то вроде этого:

std::vector<int> the_vec{4}; 

, который бы назвал список конструктор инициализатора, но почему бы не написать это, то:

std::vector<int> the_vec = {4}; 

И делать то, с кем все уже комфортно?

ответ

26

Что такое дизайн/мысль за этим синтаксисом?

С одной стороны, синтаксис бандажа позволяет избежать неприятных разборов:

T x(); // function declaration 
T x{}; // value-initialized object of type 'T' named 'x' 

В C++ 03, ближайший вы можете получить это T x((T())); или T x = T();, оба из которых требуют T, чтобы иметь доступный конструктор копирования.

+0

Хотя это хороший ответ, скажите, что они не добавили совершенно новый синтаксис для инициализации ** просто **, чтобы «исправить» наиболее неприятный синтаксический анализ: - /. –

+0

Ну, очень приятно иметь единый синтаксис инициализации (или, я полагаю, это действительно здорово, у меня не было возможности использовать его еще: -P). Из-за различных досадных парсов (как 'T x()', так и его более досадного друга 'T x (T())'), круглые скобки не могут использоваться в качестве препинатора для равномерного синтаксиса инициализации. У брекетов нет этой проблемы. –

+6

@Evan: есть причина, по которой это называется _uniform_ инициализацией. Потому что он работает везде одинаково. '{}' означает «инициализация» сейчас; в этом и есть идея. –

19

Во-первых, у вас действительно есть два варианта одной вещи:

T x = { 1, 2, 3 }; 
T x{1, 2, 3}; 

Эти два действительно делают то же инициализацию с тем исключением, что первый является недействительным, если он выбирает explicit конструктор. В противном случае они идентичны. Первая называется «копирование списка-инициализация», а вторая - «direct list-initialization».

Концепция состоит в том, что форма с = присваивает «составное значение» - значение, состоящее из 3 целых чисел. И он инициализирует x с этим значением. Для такой инициализации допускаются только конструкторы не   explicit. Концепция для x{1, 2, 3} (без знака равенства) заключается в том, что вы инициализируете переменную . 3 значения - концептуально не составное значение, а 3 отдельных значения, которые вы получаете сразу. Вы могли бы сказать, что это «вызов конструктора» в самом общем смысле этого термина.

Другой инициализации вы показали действительно что-то совершенно отличается от двух предыдущих:

T x({1, 2, 3}); 

Это только вызывает constuctors из T с {1, 2, 3} в качестве аргумента. Он не выполняет никаких причудливых действий, таких как инициализация массива, если T является массивом или инициализацией элементов структуры, если T является агрегатной структурой/классом. Если T не является классом, это объявление недействительно. Но если T имеет конструктор копирования или перемещения, он может, в свою очередь, использовать этот конструктор для создания временного T путем инициализации списка копий и привязать ссылочный параметр конструктора копирования/перемещения к этому временному. Я считаю, что вам не понадобится эта форма часто в реальном коде.


Все это записано в документах предложений комитета для списков инициализаторов. В этом случае, вы хотите взглянуть на Initializer Lists — Alternative Mechanism and Rationale, в разделе «Просмотреть программист инициализации видов»:

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

Мы находим, напротив, что это более полезно думать об этих вещах в различных условиях:

  • построении путем вызова конструктор (а «т е р-вызов»)
  • построения путем передачи значения (а «преобразование»)

(Как это происходит, бывших соответствует «прямой инициализации», а второй «копировать - инициализация ", но Термины andard в не помогают программисту.)

Позже, они находят

Заметим, что так как мы лечить { ... } в

X x = { ... }; 

как одно значение, оно не является эквивалент

X x{ ... }; 

, где { ... } - список аргументов для вызова конструктора (мы подчеркиваем его, потому что он отличается от N2531).

Правила, изложенные в C++ 0x FDIS, несколько отличаются от представленных в этой статье, но обоснование, представленное в этой статье, сохраняется и реализуется в C++ 0x FDIS.

+1

Вопрос ... вы указываете, что первая форма недействительна, если она выбирает конструктор 'Явный ', а затем во втором абзаце вы указываете, что для первой формы должны быть разрешены только конструкторы' explicit'. Кажется, эти два утверждения противоречат друг другу. Если они этого не сделают, можете ли вы объяснить, почему это так? – Jason

+0

@ Джейсон, спасибо. Я починил это. :) –

4

Данные ответы велики с теоретической точки зрения, но, возможно, несколько практических примеров тоже были бы полезны. С равномерной инициализацией теперь можно писать конструкции, которые ранее были просто невозможны.Например:

  • Инициализировать элементы массивов.

  • Глобальные постоянные контейнеры (например, карты).

А именно:

class Foo 
{ 
    int data[3]; 
public: 
    Foo() : data{1,2,3} { } 
}; 

Здесь мы можем инициализировать массив члена напрямую, без необходимости для назначения (рассмотрит ситуацию, когда строительство по умолчанию не доступно).

const std::map<int, std::string> labels { 
    { 1 , "Open" }, 
    { 2 , "Close" }, 
    { 3 , "Reboot" } }; 

Иногда универсальный глобальный объект поиска полезен, но вы не можете заполнить его данными без единой инициализации.

+0

да, но в обоих этих примерах я не мог писать: 'data ({1,2,3})' и 'const std :: map labels ({... .}); 'и оставаться в соответствии с тем, что ожидали бы люди (по крайней мере, я)? –

+0

@Evan: Ну, для массивов (или агрегатов вообще) нет синтаксиса конструктора, поэтому вы не можете иметь 'data ({1,2,3})' - я думаю, это было бы изменением. Для контейнеров вы также можете написать IL в круглых скобках, это альтернатива. –

+0

да, я бы предпочел, чтобы они добавили синтаксис конструктора в массивы, он просто лучше для меня читает –

1

Я думаю, что синтаксическая однородность очень важна в общем программировании. Например, рассмотрите,

#include <utility> 
#include <tuple> 
#include <vector> 

template <class T> 
struct Uniform { 
    T t; 
    Uniform() : t{10, 12} {} 
}; 

int main(void) 
{ 
    Uniform<std::pair<int, int>> p; 
    Uniform<std::tuple<int, int>> t; 
    Uniform<int [2]> a; 
    Uniform<std::vector<int>> v; // Uses initializer_list 

    return 0; 
} 
+0

, это хороший ответ, так что +1, но я думаю, что он мог бы быть решен лучше. Я бы лично предпочел (я знаю, что мое мнение не имеет особого значения). Если ** все ** типы могут быть инициализированы синтаксисом constructor/copy-constructor (включая массивы), и пользователь должен передать то, что другие языки называют «объектным литералом». В вашем примере, 't ({10, 12}) {}'. Я думаю, что это было бы более совместимо с языком. –

+0

К сожалению, униформа не может инициализировать std :: array . std :: array не имеет std :: initializer_list construtor – Sumant

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