1

I'm playing with tuples as compile time lists. In How can I have multiple parameter packs in a variadic template? I answered myself with some code that works in both GCC and Clang, but Clang wont compile now that I've added (what I think is) perfect forwarding. It complains that As... and as... have different lengths in std::forward<As>(as).... How can this be true when As... is the type of as...? It's As&&... as in the parameters.

#include <iostream>
#include <tuple>

template < typename ... >
struct two_impl {};

// Base case
template < typename F,
           typename ...Bs >
struct two_impl < F, std::tuple <>, std::tuple< Bs... > >  {
  void operator()(F&& f, Bs&&... bs) {
    f(std::forward<Bs>(bs)...);
  }
};

// Recursive case
template < typename F,
           typename A,
           typename ...As,
           typename ...Bs >
struct two_impl < F, std::tuple< A, As... >, std::tuple< Bs...> >  {
  void operator()(F&& f, A&& a, As&&... as, Bs&&... bs) {
    auto impl = two_impl < F, std::tuple < As&&... >, std::tuple < Bs&&..., A&& > >();
    impl(std::forward<F>(f), std::forward<As>(as)..., std::forward<Bs>(bs)..., std::forward<A>(a));
  }
};

template < typename F, typename ...Ts >
void two(F&& f, Ts&& ...ts) {
  auto impl = two_impl< F, std::tuple < Ts... >, std::tuple <> >();
  impl(std::forward<F>(f), std::forward<Ts>(ts)...);
}

struct Test {
  void operator()(int i, float f, double d) {
    std::cout << i << std::endl << f << std::endl << d << std::endl;
  }
};

int main () {
  two(Test(), 1, 1.5f, 2.1);
}

Compiling with clang -lstdc++ -std=c++0x multiple_parameter_packs.cpp

clang -lstdc++ -std=c++0x multiple_parameter_packs.cpp 
multiple_parameter_packs.cpp:24:50: error: pack expansion contains parameter packs 'As' and 'as' that have different
      lengths (1 vs. 2)
    impl(std::forward<F>(f), std::forward<As>(as)..., std::forward<Bs>(bs)..., std::forward<A>(a));
                                          ~~  ~~ ^
multiple_parameter_packs.cpp:24:5: note: in instantiation of member function 'two_impl<Test, std::tuple<float &&,
      double &&>, std::tuple<int &&> >::operator()' requested here
    impl(std::forward<F>(f), std::forward<As>(as)..., std::forward<Bs>(bs)..., std::forward<A>(a));
    ^
multiple_parameter_packs.cpp:31:3: note: in instantiation of member function 'two_impl<Test, std::tuple<int, float,
      double>, std::tuple<> >::operator()' requested here
  impl(std::forward<F>(f), std::forward<Ts>(ts)...);
  ^
multiple_parameter_packs.cpp:41:3: note: in instantiation of function template specialization
      'two<Test, int, float, double>' requested here
  two(Test(), 1, 1.5f, 2.1);
  ^
1 error generated.

Compilation exited abnormally with code 1 at Fri Mar 23 14:25:14
Community
  • 1
  • 1
Samuel Danielson
  • 5,231
  • 3
  • 35
  • 37

2 Answers2

3

This appears to be a bug in an old version of Clang. The code works fine with trunk Clang, with either libstdc++ or libc++.

$ clang++ multiple_parameter_packs.cpp -std=c++11 -stdlib=libc++
$ ./a.out
1
1.5
2.1
Richard Smith
  • 13,696
  • 56
  • 78
1

I don't think that this:

void operator()(F&& f, A&& a, As&&... as, Bs&&... bs)

is quite possible.

A parameter pack should be the last argument, and As&&... as is followed by another pack here.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Parameter packs should be at the end unless they're inside another type (like `void foo(tuple,tuple)`), but don't have to be. If they're not then they can't be automatically deduced. And when you explicitly specify them the first pack in the template parameter list seems to get all remaining template parameters at that point. later packs will be empty and if there are non-pack parameters then the template won't work because there won't be any template parameters left: `template void f(); // f can never be instantiated` – bames53 Mar 23 '12 at 20:19
  • Function parameters of operator() are in non-deducible context. The compiler has already bound e.g. to As... during deduction on the struct containing operator(). As... wont eat up remaining params of Bs... because it's arity is already deduced. – Samuel Danielson Mar 23 '12 at 21:34