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
2
  • 2
    Reduced test case: ideone.com/YA6ao Commented Mar 23, 2012 at 19:20
  • The compiler doesn't like your Bs Commented Mar 23, 2012 at 23:45

2 Answers 2

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
Sign up to request clarification or add additional context in comments.

Comments

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.

2 Comments

Parameter packs should be at the end unless they're inside another type (like void foo(tuple<Ts...>,tuple<Us...>)), 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<class... Ts,class U> void f(); // f can never be instantiated
Function parameters of operator() are in non-deducible context. The compiler has already bound e.g. <int, float, double> to As... during deduction on the struct containing operator(). As... wont eat up remaining params of Bs... because it's arity is already deduced.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.