Массив инициализации порядка оценки в С++

Мне нравятся вариативные шаблоны C++11, поэтому я часто пишу с их помощью небольшой код.

См. этот пример:

#include <cstdio>
#include <type_traits>
#include <vector>

template< typename ... T >
auto make_vector(T ... t ) -> std::vector< typename std::common_type<T...>::type >
{
    std::vector< typename  std::common_type<T...>::type > v;
    v.reserve( sizeof...(T) );

    using list = int[];
    (void)list{ 0, ( (void)v.push_back(std::move(t)) ,0)... };
    //                |/ / / /
    //                --------
    //                 \-- How are evaluated v.push_back()s, sequentially or arbitrary ?
    return v;
}

int main()
{
    auto v = make_vector(2, 3.0, 'a', 7UL );

    for(auto e : v )
      printf("%.2lf ", e);

    printf("\n");

}

В: Является ли порядок инициализации массива последовательным или произвольным (или определяется реализацией, поведение не определено)?

Если make_vector неправильно, как мне это исправить?


person Khurshid    schedule 25.11.2013    source источник
comment
@Khurshid: +1 за вопрос. Кстати, почему push_back? Почему бы не построить вектор напрямую? return {std::forward<typename std::common_type<T...>::type>(t)...};   -  person legends2k    schedule 25.11.2013
comment
@GMan: Нет, списки инициализации в фигурных скобках специально упорядочены слева направо, даже если это оценивается как обычный вызов конструктора. Он также всегда был слева направо для агрегатной инициализации.   -  person Xeo    schedule 25.11.2013
comment
@Xeo: почему-то прочитал это как вызов функции, моя ошибка!   -  person GManNickG    schedule 25.11.2013


Ответы (1)


Они оцениваются последовательно. С++ 11 § 8.5.4 [dcl.init.list], параграф 4:

В initializer-list списка braced-init-list initializer-clauses, включая все, что является результатом расширений пакета (14.5.3 ), оцениваются в том порядке, в котором они появляются.

Учитывая, что vector имеет конструктор initializer_list, вы можете упростить свою функцию до:

template <typename ... T>
auto make_vector(T ... t) ->
  std::vector< typename std::common_type<T...>::type >
{
  return { static_cast<typename std::common_type<T...>::type>(t)... };
}

и не нужно беспокоиться о тайной семантике инициализации;)

person Casey    schedule 25.11.2013
comment
Используя std::forward, вы также можете избавиться от приведения :) - person legends2k; 25.11.2013