Search code examples
c++c++11variadic-templatestemplate-meta-programmingtemplate-templates

specialization of variadic templates with class templates


Here's an issue I ran across while playing with variadic templates. I have some code that uses specialization to count "interesting" types in a parameter pack like so:

template<typename... _Pp>
struct count;

template<>
struct count<>
{
  static const int value = 0;
};

// ignore uninteresting types
template<typename _First, typename... _Rest>
struct count<_First, _Rest...>
{
  static const int value = count<_Rest...>::value;
};

// add 1 for a pointer
template<typename _First, typename... _Rest>
struct count<_First*, _Rest...>
{
  static const int value = 1 + count<_Rest...>::value;
};

// add 1 for a reference
template<typename _First, typename... _Rest>
struct count<_First&, _Rest...>
{
  static const int value = 1 + count<_Rest...>::value;
};

// add 1 for an int
template<typename... _Rest>
struct count<int, _Rest...>
{
  static const int value = 1 + count<_Rest...>::value;
};

This code works fine, but I run into problems if I want to use the same approach to count class templates:

// add 1 for a vector
template<typename... _Rest>
struct count<vector, _Rest...>
{
  static const int value = 1 + count<_Rest...>::value;
};

The above code fails to compile, error is "expected a type, got 'vector'" on the line beginning with "struct count". I'm also unable to something simpler, all class templates accepting a single argument:

// add 1 for a class template with 1 type parameter
template<template<typename> class _First, typename... _Rest>
struct count<_First, _Rest...>
{
  static const int value = 1 + count<_Rest...>::value;
}

This code also fails to compile, complaining of "expected a type, got '_First'" once again on the line beginning with "struct count". Is someone familiar with a way to accomplish this goal using this approach (i.e. some modification that I can make to one or both of the specializations that will get them to compile and perform the desired calculation at compile time)?

EDIT: I want the parameter pack for vector to be unbound, similar to the following code for a simple container wrapper with variadic template-template parameters that also specializes on std::vector:

// pass a container as a parameter using variadic template-template 

parameter
template<typename _Tp, template<typename...> class _C>
struct success
{
  // not specialized for any container
  static const bool is_specialized = false;
  // data member of container type
  _C<_Tp> c_;
};

// partial specialization of above for std::vector
template<typename _Tp>
struct success<_Tp, std::vector>
{
  // specialized for vector
  static const bool is_specialized = true;
  // again, data member of container type
  std::vector<_Tp> c_;
};

EDIT Seems like the final answer is that what I want to do can't be accomplished, but I have found a way to reframe the problem so that I cans solve it. Many thanks to those who helped.


Solution

  • If I understand correctly what you want... yes, you can create a templated struct that can count "class templates", so you can write something like

     count<std::vector, std::map, std::set, std::pair>::value
    

    but you can't mix class templates and simple typenames, so you can't write something like

     count<std::vector, int &, float, std::set>::value
    

    The problem is that if you define

     template <typename... _Pp>
        struct count;
    

    you can pass std::vector<int> to it, because std::vector<int> is a typename, but you can't pass std::vector to it because std::vector isn't a typename; it's a template<typename...> class (or template template) that it's a total different things.

    You can write something like the following struct countC

    template <template<typename...> class ...>
    struct countC;
    
    template <>
    struct countC<>
     { static const int value = 0; };
    
    // ignore uninteresting templates
    template<template<typename...> class F, template<typename...> class ... R>
    struct countC<F, R...>
     { static const int value = countC<R...>::value; };
    
    template <template<typename...> class ... R>
    struct countC<std::vector, R...>
     { static const int value = 1 + countC<R...>::value; };
    

    The following is a working complete example where I've rewritten your struct count as struct countT for count selected types, I've added a struct countC to count selected "class templates" and I've added a struct countV to count selected values of a fixed typename.

    #include <map>
    #include <set>
    #include <vector>
    #include <utility>
    #include <iostream>
    
    // countC for templates
    
    template <template<typename...> class ...>
    struct countC;
    
    template <>
    struct countC<>
     { static const int value = 0; };
    
    // ignore uninteresting templates
    template<template<typename...> class F, template<typename...> class ... R>
    struct countC<F, R...>
     { static const int value = countC<R...>::value; };
    
    template <template<typename...> class ... R>
    struct countC<std::vector, R...>
     { static const int value = 1 + countC<R...>::value; };
    
    template <template<typename...> class ... R>
    struct countC<std::map, R...>
     { static const int value = 1 + countC<R...>::value; };
    
    template <template<typename...> class ... R>
    struct countC<std::pair, R...>
     { static const int value = 1 + countC<R...>::value; };
    
    
    // countV for for values of a fixed type
    
    template <typename T, T ... v>
    struct countV;
    
    template <typename T>
    struct countV<T>
     { static const int value = 0; };
    
    // ignore uninteresting values
    template <typename T, T f, T ... r>
    struct countV<T, f, r...>
     { static const int value = countV<T, r...>::value; };
    
    // count only int odd values
    template <int f, int ... r>
    struct countV<int, f, r...>
     { static const int value = (f % 2) + countV<int, r...>::value; };
    
    
    // countT for typenames
    
    template <typename...>
    struct countT;
    
    template <>
    struct countT<>
     { static const int value = 0; };
    
    // ignore uninteresting types
    template <typename F, typename ... R>
    struct countT<F, R...>
     { static const int value = countT<R...>::value; };
    
    template <typename F, typename ... R>
    struct countT<F*, R...>
     { static const int value = 1 + countT<R...>::value; };
    
    template<typename F, typename ... R>
    struct countT<F&, R...>
     { static const int value = 1 + countT<R...>::value; };
    
    template<typename ... R>
    struct countT<int, R...>
     { static const int value = 1 + countT<R...>::value; };
    
    
    int main()
     {
       std::cout << "countC vector + map + set + pair                   = " 
          << countC<std::vector, std::map, std::set, std::pair>::value
          << std::endl;
    
       std::cout << "countT int + float + bool* + double& + bool + int& = " 
          << countT<int, float, bool*, double&, bool, int&>::value
          << std::endl;
    
       std::cout << "countV int, 1 + 4 + 4 + 5 + 7 + 10 + 11 + 16 + 15  = " 
          << countV<int, 1, 4, 4, 5, 7, 10, 11, 16, 15>::value
          << std::endl;
    
       std::cout << "countV long, 1 + 4 + 4 + 5 + 7 + 10 + 11 + 16 + 15 = " 
          << countV<long, 1, 4, 4, 5, 7, 10, 11, 16, 15>::value
          << std::endl;
    
       return 0;
     }
    

    p.s.: sorry for my bad English.