通过gcc和clang进行多参数包扩展

时间:2017-01-30 15:39:25

标签: c++ gcc clang variadic-templates

我正在尝试使用variadics将N参数函数转换为2^N参数函数。以下代码段由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一起使用?

2 个答案:

答案 0 :(得分:2)

此代码与[temp.deduct.type]:

发生冲突
  

非推断的上下文是:[...]一个函数参数包,它不会出现在 parameter-declaration-list 的末尾。

如:

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)将被推导为空的模板参数序列。

这种演绎 - 非诱导包装为空的想法会以不同的方式破坏你在gcc和clang上的代码。考虑通话apply(1,2)

  • 我尝试过的每个gcc版本都认为Ds... ds包为空,并将Ds...推断为<int>,将Is...推断为<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...);
}

这适用于每个编译器。

答案 1 :(得分:0)

所以我玩了一些代码并提出了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);
}

有趣的是,这两种类型列表都是必需的,尽管似乎只有一种类型可以解决歧义。

接下来是Barry的回答。它用gcc编译,但对我来说却失败了:

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);
}

希望有人觉得这很有帮助。