web-dev-qa-db-ja.com

可変長テンプレートに複数のパラメーターパックを含めるにはどうすればよいですか?

関数one()は、1つのパラメーターパックを受け入れます。関数two()は2つを受け入れます。各パックは、タイプ[〜#〜] a [〜#〜]および[〜#〜] b [〜#〜]にラップされるように制限されています。 two()をインスタンス化することが不可能なのはなぜですか?

template <typename T>
struct A {};

template <typename T>
struct B {};

template <typename... Ts>
void one(A<Ts> ...as) {
}

template <typename... Ts, typename... Us>
void two(A<Ts> ...as, B<Us> ...bs) {
}

int main() {
  auto a = A<int>();
  auto b = B<int>();

  // Just fine
  one();
  one(a);
  one(a, a);

  // All errors    
  two();
  two(a);
  two(a, b);
}

Gccとclangで試しました。

sam@wish:~/x/cpp$ gcc -std=c++0x variadic_templates.cpp 
variadic_templates.cpp: In function ‘int main()’:
variadic_templates.cpp:23:7: error: no matching function for call to ‘two()’
variadic_templates.cpp:23:7: note: candidate is:
variadic_templates.cpp:11:6: note: template<class ... Ts, class ... Us> void two(A<Ts>..., B<Us>...)
variadic_templates.cpp:24:8: error: no matching function for call to ‘two(A<int>&)’
variadic_templates.cpp:24:8: note: candidate is:
variadic_templates.cpp:11:6: note: template<class ... Ts, class ... Us> void two(A<Ts>..., B<Us>...)
variadic_templates.cpp:25:11: error: no matching function for call to ‘two(A<int>&, B<int>&)’
variadic_templates.cpp:25:11: note: candidate is:
variadic_templates.cpp:11:6: note: template<class ... Ts, class ... Us> void two(A<Ts>..., B<Us>...)
sam@wish:~/x/cpp$ clang -std=c++0x variadic_templates.cpp 
variadic_templates.cpp:23:3: error: no matching function for call to 'two'
  two();
  ^~~
variadic_templates.cpp:11:6: note: candidate function template not viable: requires at least 1 argument, but 0 were provided                                                                                                                 
void two(A<Ts> ...as, B<Us> ...bs) {}
     ^
variadic_templates.cpp:24:3: error: no matching function for call to 'two'                                                                                                                                                                   
  two(a);
  ^~~
variadic_templates.cpp:11:6: note: candidate function not viable: requires 0 arguments, but 1 was provided                                                                                                                                   
void two(A<Ts> ...as, B<Us> ...bs) {}
     ^
variadic_templates.cpp:25:3: error: no matching function for call to 'two'                                                                                                                                                                   
  two(a, b);
  ^~~
variadic_templates.cpp:11:6: note: candidate function not viable: requires 0 arguments, but 2 were provided                                                                                                                                  
void two(A<Ts> ...as, B<Us> ...bs) {}
     ^
3 errors generated.
47

テンプレートテンプレートパラメータを使用していくつかのパラメータパックを作成する別の方法を次に示します。

#include <iostream>

template <typename... Types>
struct foo {};

template < typename... Types1, template <typename...> class T
         , typename... Types2, template <typename...> class V
         , typename U >
void
bar(const T<Types1...>&, const V<Types2...>&, const U& u)
{
  std::cout << sizeof...(Types1) << std::endl;
  std::cout << sizeof...(Types2) << std::endl;
  std::cout << u << std::endl;
}

int
main()
{
  foo<char, int, float> f1;
  foo<char, int> f2;
  bar(f1, f2, 9);
  return 0;
}
34
Alexandre Hamez

1つの解決策を見つけました。各パラメーターパックをタプルでラップします。部分的な特殊化には構造体を使用します。これは、1つのTupleをリストとして消費し、別のTupleを累積することにより、引数をファンクターに転送するデモです。さて、これはコピーして転送します。タプルは型の推測に使用されますが、関数パラメーターにはタプルは使用されません。

#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(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(f, as..., bs..., a);
  }
};

template < typename F, typename ...Ts >
void two(F f, Ts ...ts) {
  auto impl = two_impl< F, std::Tuple < Ts... >, std::Tuple <> >();
  impl(f, 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);
}

タプルは非常に優れたコンパイル時間リストです。

15

関数テンプレート(skypjackの例など)およびクラステンプレートと変数テンプレートの部分的な特殊化は、テンプレートパラメーターパックに続く各テンプレートパラメーターがデフォルト値を持っているか、推測できる場合、複数のパラメーターパックを持つことができます。追加/指摘したいのは、クラスおよび変数テンプレートには部分的な専門化が必要なことだけです。 (参照:C++テンプレート、The Complete Guide、Vandevoorde、Josuttis、Gregor 12.2.4、Second Edition)

// A template to hold a parameter pack
template < typename... >
struct Typelist {};

// Declaration of a template
template< typename TypeListOne 
        , typename TypeListTwo
        > 
struct SomeStruct;

// Specialization of template with multiple parameter packs
template< typename... TypesOne 
        , typename... TypesTwo
        >
struct SomeStruct< Typelist < TypesOne... >
                 , Typelist < TypesTwo... >
                 >
{
        // Can use TypesOne... and TypesTwo... how ever
        // you want here. For example:
        typedef std::Tuple< TypesOne... > TupleTypeOne;
        typedef std::Tuple< TypesTwo... > TupleTypeTwo;
};      
7