Параметры Variadic для списка инициализаторов запятой?

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

class A{
    public:
    template<typename ...T>
    A(T ... values): arr(sizeof...(T)) {
         //convert the values somehow that the parameter pack is expanded in the comma initialized list as the following:

         //arr << values1, values2, values3,... , valuesN
    }

    ArrayType arr;

}

Этот метод инициализации запятой особенно актуален для ArrayType, являющегося классом Eigen::Matrix ( arr << 1,2,3; ). Мне было интересно, выполнимо ли следующее и есть ли какой-то другой элегантный способ заполнить массив в случае, если мы можем использовать оператор индекса (i) для i-го элемента :-)

Большое спасибо :)


person Gabriel    schedule 06.03.2014    source источник
comment
Если бы вы разрешили arr << values1; arr << values2;, это было бы легко. В противном случае я не вижу короткого способа сделать это без написания шаблонов рекурсивных функций.   -  person Johannes Schaub - litb    schedule 07.03.2014
comment
@JohannesSchaub-litb, а что не так с рекурсивной функцией template?   -  person Yakk - Adam Nevraumont    schedule 07.03.2014
comment
@Yakk, это не кажется компактным   -  person Johannes Schaub - litb    schedule 07.03.2014


Ответы (1)


#include <utility>

template<typename T0, typename... Ts>
struct comma_collapse { typedef T0 type; };
template<typename T0, typename T1, typename... Ts>
struct comma_collapse<T0, T1, Ts...> {
    typedef decltype( std::declval<T0>(), std::declval<typename comma_collapse<T1, Ts...>::type>() ) type;
};

template<typename LHS>
LHS&& comma_splice(LHS&& lhs){ return std::forward<LHS>(lhs); }

template<typename LHS, typename RHS, typename... Tail>
typename comma_collapse<LHS,RHS,Tail...>::type&& comma_splice( LHS&& lhs, RHS&& rhs, Tail&&... tail )
{
  auto&& first_two = (std::forward<LHS>(lhs),std::forward<RHS>(rhs));
  return comma_splice(
    std::forward<decltype(first_two)>(first_two),
    std::forward<Tail>(tail)...
  );
}

Затем реализуйте конструктор следующим образом:

template<typename T0, typename... Ts>
A(T0&& t0, Ts&& ... ts): arr(sizeof...(Ts)) {
  comma_splice( (arr << std::forward<T0>(t0)), std::forward<Ts>(ts)... );
}

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

живой пример

person Yakk - Adam Nevraumont    schedule 06.03.2014
comment
comma_collapse существует только для решения проблемы, заключающейся в том, что и GCC, и CLANG отказываются проверять сам comma_splice, когда я использую его рекурсивно в определении типа возвращаемого значения comman_splice, и хочу использовать только более раннюю перегрузку. Я не знаю, правы ли они. Поэтому я вручную решаю, какой тип будет в comma_collapse. - person Yakk - Adam Nevraumont; 07.03.2014
comment
Для рекурсивного обращения к возвращаемому типу в шаблоне функции требуется ADL, так как возвращаемый тип является частью декларатора, и поэтому имя (шаблона функции) нельзя найти с помощью неквалифицированного поиска. - person dyp; 08.03.2014
comment
@dyp Хм. То есть, если я поставлю его в namespace detail с игрушечным аргументом struct detail::Foo, это сработает? - person Yakk - Adam Nevraumont; 09.03.2014
comment
Я так думаю. Несколько месяцев назад я ответил на вопрос, касающийся этой проблемы, посмотрю, смогу ли я его найти. - person dyp; 09.03.2014
comment
Хм, кажется, это был (один) из вопросов, которые я имел в виду. Однако это не распространяется на ваш обходной путь. - person dyp; 09.03.2014