C ++ 0x равномерная инициализация "странности" - PullRequest
17 голосов
/ 11 августа 2011

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

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

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};

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

Ответы [ 4 ]

26 голосов
/ 11 августа 2011

Каков дизайн / идея этого синтаксиса?

Во-первых, синтаксис скобок позволяет избежать неприятных синтаксических разборов:

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

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

19 голосов
/ 12 августа 2011

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

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

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

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

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

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

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


Все это записано в предложениях комитета по спискам инициализаторов. В этом случае вам нужно взглянуть на Списки инициализаторов - альтернативный механизм и обоснование , в разделе «Взгляд программиста на виды инициализации»:

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

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

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

(Как оказалось, первое соответствует «прямой инициализации», а второе «копировать инициализация ", но условия стандарта не помогают программисту.)

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

Обратите внимание, что поскольку мы рассматриваем { ... } в

X x = { ... };

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

X x{ ... };

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

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

4 голосов
/ 12 августа 2011

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

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

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

То есть:

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

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

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

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

1 голос
/ 15 августа 2011

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

#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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...