расширение пакета с несколькими параметрами gcc и clang
Я пытаюсь использовать variadics для преобразования функции N
параметров в функцию параметров 2^N
Следующий фрагмент nvcc 8.0
компилируется clang 3.9
, в то время как nvcc 8.0
(эффективно gcc 5.4
) терпит неудачу с ошибкой:
error: no instance of overloaded function "foo" matches the argument list
код:
template<class... Ts> struct Typelist{};
template <class..., class... Us>
void foo(Typelist<>, Typelist<Us...>, Us... us ){
// do the actual work
}
template <class T, class... Ts, class... Us>
void foo(Typelist<T, Ts...>, Typelist<Us...>, T t, Ts... ts, Us... us){
foo(Typelist<Ts...>{}, Typelist<Us..., Us...>{}
, ts..., us..., (us+t)...);
}
template <class... Ts>
void bar(Ts... ts){
foo(Typelist<Ts...>{}, Typelist<unsigned>{}
, ts..., 0u);
}
называемый
int main(int /*argc*/, char */*argv*/[])
{
bar(2u);
bar(2u, 3u, 4u);
return 0;
}
Я делаю что-то неправильно? Как мне заставить работать с gcc
?
Этот код работает от [temp.deduct.type]:
Невыводимые контексты: [...] Пакет параметров функции, который не встречается в конце списка параметров-объявления.
как в:
template<class D0, class... Ds, class... Is>
HOST_DEVICE void _apply(Typelist<D0, Ds...> , D0 d0, Ds... ds, Is... is) {
// ~~~~~~~~
и [temp.arg.explicit]:
Исходный шаблон шаблонов шаблонов (14.5.3), не выведенный иначе, будет выведен на пустую последовательность аргументов шаблона.
Эта идея deduce-nededuced-packs-as-empty разбивает ваш код как на gcc, так и на clang по-разному. Рассмотрим apply(1,2)
вызова apply(1,2)
:
- каждая версия gcc, которую я пробовал, рассматривает
Ds... ds
pack empty и выводит Ds...
как <int>
и Is...
as <int, unsigned int>
. Так что перегрузка выбрасывается, так как она принимает 5 аргументов, и мы проезжаем только 4. каждая версия clang, которую я пробовал, выводит второй Ds...
как <int>
из первого аргумента и <>
из пакета и рассматривает вывод как неудачу из-за несогласованности. В любом случае, здесь нет пути.
Вместо этого вы можете перевернуть заказ и сначала поместить все Is...
Поскольку вы знаете все типы, вы можете явно указать их, и пусть Ds...
будет выведено. То есть:
template<class... Is>
HOST_DEVICE void _apply(Is..., Typelist<>)
{
// ...
}
template<class... Is, class D0, class... Ds>
HOST_DEVICE void _apply(Is... is, Typelist<D0, Ds...> , D0 d0, Ds... ds) {
_apply<Is..., decltype(std::declval<Is>()+std::declval<D0>())...>(
is...,
(is+d0)...,
Typelist<Ds...>{},
ds...);
}
template<class... Ds>
HOST_DEVICE void apply(Ds... ds) {
_apply<unsigned int>(0u, Typelist<Ds...>{}, ds...);
}
Это работает на каждом компиляторе.
Поэтому я немного сыграл с кодом и придумал 3 версии:
Компиляция в clang, не выполняется с помощью gcc:
template <class..., class... Us>
void foo(Typelist<>, Typelist<Us...>, Us... us ){ /*do the stuff*/ }
template <class T, class... Ts, class... Us>
void foo(Typelist<T, Ts...>, Typelist<Us...>, T t, Ts... ts, Us... us){
foo(Typelist<Ts...>{}, Typelist<Us..., Us...>{}, ts..., us..., (us+t)...);
}
template <class... Ts>
void bar(Ts... ts){
foo(Typelist<Ts...>{}, Typelist<unsigned>{}, ts..., 0u);
}
Интересно, что для этого требуются оба списка типов, хотя кажется, что одного достаточно, чтобы устранить двусмысленность.
Следующий из ответа Барри. Он компилируется с помощью gcc, но мне не хватает clang:
template<class... Is>
void foo(Is..., Typelist<>) { /*do the stuff*/ }
template<class... Is, class D0, class... Ds>
void foo(Is... is, Typelist<D0, Ds...> , D0 d0, Ds... ds) {
foo<Is..., decltype(std::declval<Is>()+std::declval<D0>())...>(
is...,
(is+d0)...,
Typelist<Ds...>{},
ds...);
}
template<class... Ds>
void bar(Ds... ds) {
foo<unsigned int>(0u, Typelist<Ds...>{}, ds...);
}
И, наконец, тот, кто работает с gcc (5.4, 6.3) и clang (3.9):
template<class... Us>
struct foo_impl<Typelist<>, Typelist<Us...>>{
auto operator()(Us... us)-> void { /*do the stuff here*/ }
};
template<class T, class... Ts, class... Us>
struct foo_impl<Typelist<T, Ts...>, Typelist<Us...>>{
auto operator()(T t, Ts... ts, Us... us)-> void{
foo_impl<Typelist<Ts...>, Typelist<Us..., Us...>>{}(ts..., us..., (us+t)...);
}
};
template <class... Ts>
void bar(Ts... ts){
foo_impl<Typelist<Ts...>, Typelist<unsigned>>{}(ts..., 0u);
}
Надеюсь, кто-то найдет это полезным.