Deqing
Deqing

Reputation: 14632

nested templates (template template parameters)

How to write a template function that accepts nested templates?

For example, I'd like to write following function:

void print(const T1<T2<T3>> &container);

Had tried:

template<
  template<typename> class T1,
  template<typename> class T2,
  class T3
>
void print(const T1<T2<T3>> &container) {
  for (auto &e : container)
    for (auto x : e)
      std::cout<<e<<' ';
  std::cout<<'\n';
}

int main()
{
  std::vector<std::deque<int>> c = {{1,2},{3}};
  print(c);
  return 0;
}

Compile error from g++:

a.cc: In function ‘int main()’:
a.cc:23:14: error: no matching function for call to ‘print(std::vector<std::deque<int> >&)’
       print(c);
              ^
a.cc:12:10: note: candidate: template<template<class> class T1, template<class> class T2, class T3> void print(const T1<T2<T3> >&)
     void print(const T1<T2<T3>> &container) {
          ^
a.cc:12:10: note:   template argument deduction/substitution failed:
a.cc:23:14: error: wrong number of template arguments (2, should be 1)
       print(c);
              ^
a.cc:8:32: note: provided for ‘template<class> class T1’
       template<typename> class T1,
                                ^

Compile error from Clang:

a.cc:23:7: error: no matching function for call to 'print'
      print(c);
      ^~~~~
a.cc:12:10: note: candidate template ignored: substitution failure : template template argument has different template parameters than its corresponding
      template template parameter
    void print(const T1<T2<T3>> &container) {
         ^

Also tried:

template<
  template<template<typename> class> class T1,
  template<typename> class T2,
  class T3
>
void print(const T1<T2<T3>> &container);

but it still has compile error even before deduction:

a.cc:12:25: error: template argument for template template parameter must be a class template or type alias template
    void print(const T1<T2<T3>> &container) {
                        ^

---- EDIT ----

What if I'd like to return a pointer to one of it's type?

 T3 get(const T1<T2<T3>> &container);

Upvotes: 3

Views: 5145

Answers (2)

WhiZTiM
WhiZTiM

Reputation: 21576

Though, I'll prefer you use a single template and pick the typedefs from it.

Something like:

template<typename T1>
void print(const T1& container) { //As opposed to const T1<T2<T3>> &container
    using T2 = typename T1::value_type;
    using T3 = typename T2::value_type;

  for (auto &e : container)
    for (auto x : e)
      std::cout<<x<<' ';
  std::cout<<'\n';
}

But if you must go your way, then this will work (std::vector and std::deque are actually declared with two template parameters, though the allocator is defaulted):

template<
  template<typename, typename> class T1,
  template<typename, typename> class T2,
  typename AllocT1, typename AllocT2, 
  typename T3
>
void print(const T1<T2<T3, AllocT2>, AllocT1> &container) {
  for (auto &e : container)
    for (auto x : e)
      std::cout<<x<<' ';
  std::cout<<'\n';
}

But there is a neater solution:

template<typename T1, 
         typename T2 = typename T1::value_type,
         typename T3 = typename T2::value_type>
T3 print(const T1& container){

  for (auto &e : container)
    for (auto x : e)
      std::cout<<x<<' ';
  std::cout<<'\n';

  return T3();
}

The choice is expressly yours. :-)

EDIT:

What if I'd like to return a pointer to one of it's type?

T3 get(const T1<T2<T3>> &container);

In C++14, you can simply use an auto placeholder return type, or use the latter solution above.

Upvotes: 7

Dimitrios Bouzas
Dimitrios Bouzas

Reputation: 42899

A single template parameter will work for you:

template<class T>
void print(const T& container) {
  for (auto &e : container)
    for (auto x : e) std::cout << x << ' ';
  std::cout << '\n';
}

Live Demo

Upvotes: 3

Related Questions