Почему для инициализации массива пар все еще нужны двойные скобки в C ++ 14? - PullRequest
0 голосов
/ 30 мая 2018

При использовании стандарта C ++ 14 инициализация std::array может выполняться с одиночными скобками (см. http://en.cppreference.com/w/cpp/container/array):

. Однако это не работает для std::array из std::pair.

Почему они работают:

std::pair<int, int> p { 1, 2 };
std::array<int, 3> a {1, 2, 3};

, но это не работает:

std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};

, пока это работает снова?

std::array<std::pair<int, int>, 3> b {{{1, 11}, {2, 22}, {3, 33}}};

Кроме того, для завершения инициализация старого доброго массива работает с одиночными скобками

std::pair<int, int> c[3] {{1, 11}, {2, 22}, {3, 33}};

Ответы [ 5 ]

0 голосов
/ 30 мая 2018

Правило исключения C ++ 14 применяется только для инициализации субагрегата.

Так, например, работает примерно так:

std::array<std::array<int, 3>, 3> a{1, 11, 2, 22, 3, 33};

Здесь совокупностьагрегаты могут быть инициализированы списком без дополнительных фигурных скобок.

Но std::pair не является агрегатом (у него есть конструкторы), поэтому правило не применяется.

Какиеозначает, что без правила исключения фигурных скобок std::array, сам по себе являющийся агрегатом с массивом внутри, требуется дополнительный набор фигурных скобок для инициализации списка .Помните, что шаблон класса array реализован следующим образом:

template<typename T, std::size_t N> 
struct array {
  T elems[N];
};

Чтобы инициализировать список без правила скобок, вам нужен дополнительный набор скобок, чтобы добраться до elems член.

0 голосов
/ 30 мая 2018

Похоже, что это неоднозначность синтаксического анализа, несколько похожая на знаменитый самый неприятный синтаксический анализ .Я подозреваю, что происходит:

Если вы пишете

std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};

, компилятор может интерпретировать синтаксис двумя способами:

  1. Вы выполняетеполная инициализация скобки (то есть самая внешняя скобка относится к совокупной инициализации std::array, в то время как первая внутренняя скобка инициализирует представление внутреннего члена std::array, которое является реальным C-массивом).Это не удастся скомпилировать, так как std::pair<int, int> впоследствии не может быть инициализирован с помощью 1 (все скобки израсходованы).clang выдаст ошибку компилятора, точно указав, что:

    error: no viable conversion from 'int' to 'std::pair<int, int>'
     std::array<std::pair<int, int>, 3> a{{1, 11}, {2, 22}, {3, 33}};
                                              ^
    

    Обратите внимание, что эта проблема решена, если нет внутреннего агрегата-члена, который нужно инициализировать, то есть

    std::pair<int, int> b[3] = {{1, 11}, {2, 22}, {3, 33}};
    

    скомпилируется очень хорошокак агрегатная инициализация.

  2. (так, как вы это имели в виду.) Вы выполняете инициализацию с фигурными скобками, поэтому самые внутренние скобки предназначены для агрегатной инициализации отдельных пар, а скобки дляпредставления внутреннего массива исключены.Обратите внимание, что даже если бы не было этой неоднозначности, как правильно указано в ответ rustyx , правила исключения скобок не применяются, поскольку std::pair не является агрегатным типом, поэтому программа все равно будет некорректной.

Компилятор предпочтет вариант 1. Предоставляя дополнительные фигурные скобки, вы выполняете инициализацию полной фигурной скобки и снимаете любую синтаксическую неоднозначность.

0 голосов
/ 30 мая 2018

Теоретически std::array следует инициализировать с помощью общей инициализации.Так что на самом деле это:

std::array<int, 3> a {1, 2, 3};

является синтаксическим сахаром для этого:

std::array<int, 3> a {{1, 2, 3}};

Как видите, в первом случае кажется, что я инициализирую массив значениями, но он действительно агрегированинициализация с инициализированным списком.Это ясно как день во второй ситуации.Так что для начала.

Хорошо, так почему же это не работает?

std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};

Ну, проще говоря - компилятор не может определить, какой тип синтаксиса используется для инициализации массива,{1, 11} может интерпретироваться как список инициализатора и использовать первую версию, так и в виде пары и идти со второй версией.

Этот код:

std::array<std::pair<int, int>, 3> b {{{1, 11}, {2, 22}, {3, 33}}};.

устраняет неоднозначность.

Источник: http://en.cppreference.com/w/cpp/language/aggregate_initialization

0 голосов
/ 30 мая 2018

Без двойных скобок утверждение просто двусмысленное.Рассмотрим следующий код:

    std::array<std::pair<int, int>, 1> a = {{ {1, 2} }};
    std::array<int, 2> b = { {1, 2} };

Без двойных скобок в первом определении компилятор будет обрабатывать { {1,2} } как скалярный список инициализации для array<int, 2>.Вам нужно объявить явный вложенный фигурный список инициализации , чтобы компилятор мог распознать, что внутренний список также агрегатно-инициализирован (против скалярной инициализации), так что онможно построить массив std::pair.

0 голосов
/ 30 мая 2018

Я собираюсь угадать здесь.
Список инициализатора для std::array<T,n> должен быть списком T (или просто создать T).Так что вы могли бы сделать

std::array<std::pair<int,int>,3> b { std::pair{1,11}, std::pair{2,22}, std::pair{3,33} };

, но это утомительно многословно.Чтобы получить преобразование в std::pair<int,int>, которое вы хотите, вам нужно предоставить список инициализаторов, поэтому

std::array<std::pair<int,int>,3> b {
    { // element 1
      { // initialize from:
        { 1,11 } // std::initializer_list
       }
     },
  ...
};

Я не могу защищать это дальше, но учтите, что std::vector<T, Allocator>::vector( std::initializer_list<T>, const Allocator& alloc=Allocator()) определено, но std::array<T,n>::array( std::initializer_list<T> ) не является.Также не определено std::pair<U,T>::pair( std::initializer_list<??> ).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...