C++ variadic template template argument that matches any kind of parameters

試著忘記壹切 提交于 2019-12-02 17:16:26

Your interesting construct has two levels with variadic templates.

  • An outer variadic template parameter list TemplateP & Sizes for a function template
  • An inner parameter pack as the template parameters of your template template parameter TemplateT, a class template

First, let's look at the inner TemplateT class: why can the ellipsis operator not not match something like TemplateT< int, 2 >? Well, the standard defines variadic templates in §14.5.3 as

template<class ... Types> struct Tuple { };
template<T ...Values> struct Tuple2 { };

where the template argument pack in the first case may only match types and in the second version only values of type T. In particular,

Tuple < 0 >    error;  // error, 0 is not a type!
Tuple < T, 0 > error2; // T is a type, but zero is not!
Tuple2< T >    error3; // error, T is not a value
Tuple2< T, 0 > error4; // error, T is not a value

are all malformed. Furthermore, it is not possible to fall back to something like

template<class ... Types, size_t ...Sizes> struct Tuple { };

because the standard states in §14.1.11:

If a template-parameter of a primary class template or alias template is a template parameter pack, it shall be the last template-parameter. A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template or has a default argument (14.8.2).

In other words, for class templates only one variadic parameter pack may appear in the definition. Therefore the above (double)-variadic class definition is malformed. Because the inner class always needs such a combination, it is impossible to write something as general as you conceived.


What can be rescued? For the outer function template, some shards can be put together, but you won't like it. As long as the second parameter pack can be deduced from the first, two parameter packs may appear (in a function template). Therefore, a function such as

template < typename... Args, size_t... N > void g(const std::array< Args, N > &...arr);
g(std::array< double, 3 >(), std::array< int, 5>());

is allowed, because the integer values can be deduced. Of course, this would have to be specialized for every container type and is far from what you had imagined.

You must have a metafunction that rebinds the type of container. Because you cannot just replace first template parameter:

vector<int, allocator<int> > input;
vector<double, allocator<int> > just_replaced;
vector<double, allocator<double> > properly_rebound;

So, just write such a metafunction for known set of containers.

template<class Container, class NewValue> class rebinder;

// example for vectors with standard allocator
template<class V, class T> class rebinder< std::vector<V>, T > {
public:
  typedef std::vector<T> type;
};
// example for lists with arbitrary allocator
template<class V, class A, class T> class rebinder< std::list<V,A>, T > {
  typedef typename A::template rebind<T>::other AT; // rebind the allocator
public:
  typedef std::list<T,AT> type; // rebind the list
};
// example for arrays
template<class V, size_t N> class rebinder< std::array<V,N>, T > {
public:
  typedef std::array<T,N> type;
};

Rules of rebinding may vary for different containers.

Also you might require a metafunction that extracts value type from arbitrary container, not only std-conformant (typedef *unspecified* value_type)

template<class Container> class get_value_type {
public:
  typedef typename Container::value_type type; // common implementation
};
template<class X> class get_value_type< YourTrickyContainer<X> > {
  ......
public:
  typedef YZ type;
};
bit2shift

It would be awesome if we had such thing, as it would allow us to write a is_same_template trait in a breeze.
Until then, we specialize all the way.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!