Объединяя несколько идей из предыдущих постов, вот решение, которое работает даже для вложенных конструкций (протестировано в GCC4.6):
template <typename T, typename ...Args>
std::array<T, sizeof...(Args) + 1> make_array(T && t, Args &&... args)
{
static_assert(all_same<T, Args...>::value, "make_array() requires all arguments to be of the same type."); // edited in
return std::array<T, sizeof...(Args) + 1>{ std::forward<T>(t), std::forward<Args>(args)...};
}
Как ни странно, не может сделать возвращаемое значение ссылкой на rvalue, чтоне будет работать для вложенных конструкций.В любом случае, вот тест:
auto q = make_array(make_array(make_array(std::string("Cat1"), std::string("Dog1")), make_array(std::string("Mouse1"), std::string("Rat1"))),
make_array(make_array(std::string("Cat2"), std::string("Dog2")), make_array(std::string("Mouse2"), std::string("Rat2"))),
make_array(make_array(std::string("Cat3"), std::string("Dog3")), make_array(std::string("Mouse3"), std::string("Rat3"))),
make_array(make_array(std::string("Cat4"), std::string("Dog4")), make_array(std::string("Mouse4"), std::string("Rat4")))
);
std::cout << q << std::endl;
// produces: [[[Cat1, Dog1], [Mouse1, Rat1]], [[Cat2, Dog2], [Mouse2, Rat2]], [[Cat3, Dog3], [Mouse3, Rat3]], [[Cat4, Dog4], [Mouse4, Rat4]]]
(Для последнего вывода я использую мой pretty-printer .)
Собственно, давайте улучшимТип безопасности этой конструкции.Нам определенно нужно, чтобы все типы были одинаковыми.Один из способов - добавить статическое утверждение, которое я редактировал выше.Другой способ - включить make_array
, только если типы одинаковы, например:
template <typename T, typename ...Args>
typename std::enable_if<all_same<T, Args...>::value, std::array<T, sizeof...(Args) + 1>>::type
make_array(T && t, Args &&... args)
{
return std::array<T, sizeof...(Args) + 1> { std::forward<T>(t), std::forward<Args>(args)...};
}
В любом случае вам понадобится признак типа * variadic all_same<Args...>
.Здесь это обобщение из std::is_same<S, T>
(обратите внимание, что затухание важно для обеспечения возможности смешивания T
, T&
, T const &
и т. Д.):
template <typename ...Args> struct all_same { static const bool value = false; };
template <typename S, typename T, typename ...Args> struct all_same<S, T, Args...>
{
static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value && all_same<T, Args...>::value;
};
template <typename S, typename T> struct all_same<S, T>
{
static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value;
};
template <typename T> struct all_same<T> { static const bool value = true; };
Обратите внимание, что make_array()
возвращает значениеВременная копия, которую компилятору (с достаточным количеством флагов оптимизации!) разрешено рассматривать как значение или как-либо иначе оптимизировать, а std::array
является агрегатным типом, поэтому компилятор может выбрать лучший из возможных методов построения.
Наконец, обратите внимание, что вы не можете избежать конструкции копирования / перемещения, когда make_array
устанавливает инициализатор.Таким образом, std::array<Foo,2> x{Foo(1), Foo(2)};
не имеет копирования / перемещения, но auto x = make_array(Foo(1), Foo(2));
имеет два копирования / перемещения, поскольку аргументы передаются в make_array
.Я не думаю, что вы можете улучшить это, потому что вы не можете лексически передать список инициализатора переменной помощнику и , чтобы определить тип и размер - если препроцессор имел функцию sizeof...
для аргументов переменнойвозможно, это можно было бы сделать, но не на базовом языке.