简体   繁体   English

匹配任何类型参数的 C++ 可变参数模板模板参数

[英]C++ variadic template template argument that matches any kind of parameters

I was wondering if it's possible to write a template function that can take any other arbitrary template as a parameter and properly match the template name (ie not just the resulting class).我想知道是否有可能编写一个模板函数,它可以将任何其他任意模板作为参数并正确匹配模板名称(即不仅仅是生成的类)。 What I know to work is this:我所知道的工作是这样的:

template<template<typename ...> class TemplateT, typename... TemplateP>
void f(const TemplateT<TemplateP...>& param);

Which will match for instance for f(std::vector<int>()) or f(std::list<int>()) but will not work for f(std::array<int, 3>()) , as the second parameter is a size_t and no type.例如,这将匹配f(std::vector<int>())f(std::list<int>())但不适用于f(std::array<int, 3>()) ,因为第二个参数是size_t并且没有类型。

Now I guess one could do something crazy like:现在我想人们可以做一些疯狂的事情,比如:

template<template<typename ...> class TemplateT, size... Sizes, typename... TemplateP>
void f(const TemplateT<Sizes..., TemplateP...>& param);

Hoping that the compiler would properly derive either the TemplateP ellipsis or the Sizes ellipsis to be empty.希望编译器能够正确地将TemplateP省略号或Sizes省略号导出为空。 But not only is it ugly, it also will still just work for templates that take either types or size_t parameters.但它不仅丑陋,而且它仍然只适用于采用 types 或size_t参数的模板。 It still won't match arbitrary templates for instance with bool parameters.它仍然不会匹配任意模板,例如带有bool参数。

Same goes for an approach with overloading:重载方法也是如此:

template<template<typename ...> class TemplateT, typename... TemplateP>
void f(const TemplateT<TemplateP...>& param);

template<template<typename ...> class TemplateT, size... Sizes>
void f(const TemplateT<Sizes...>& param);

Furthermore, such approach wont' work if we would like to mix size_t and typenames .此外,如果我们想混合使用size_ttypenames ,这种方法将不起作用。 So what would be required to match anything would be something like this, where there are no constraints at all to what is allowed in the ellipsis:因此,匹配任何内容所需的内容将是这样的,其中对省略号中允许的内容完全没有限制:

template<template<...> class TemplateT, ... Anything>
void f(const TemplateT<Anything...>& param);

That syntax doesn't work but maybe there's other syntax to define something like this?该语法不起作用,但也许还有其他语法来定义这样的东西?

This is mainly me wondering what is possible in the language, thought there might actually be a use for it, if you have different templates where the first parameter is always fixed and you would like to change it based on the return type and keep everything else.这主要是我想知道语言中有什么可能,认为它实际上可能有用,如果您有不同的模板,其中第一个参数总是固定的,并且您想根据返回类型更改它并保留其他所有内容. Something like this:像这样的东西:

template<
    template<typename ValueT, ...> class TemplateT,
    ... Anything,
    typename ValueT,
    typename ResultT = decltype(some_operation_on_value_t(std::declval<ValueT>())>
TemplateT<ResultT, Anything...> f(const TemplateT<ValueT, Anything...>& in);

So, any way to make this work in a completely generic way using pattern matching?那么,有什么方法可以使用模式匹配以完全通用的方式完成这项工作?

This is not purely a thought experiment, as the use case for this where I was stuck was to create pure functional primitives that operate on containers and will implicitly construct immutable result containers.这不仅仅是一个思想实验,因为我遇到的用例是创建对容器进行操作的纯函数原语,并将隐式构造不可变的结果容器。 If the result container has a different data type we need to know the type the container operates on, so the only requirement on any container would be that the first parameter of the template needs to be the input type so it can be replaced with a different output type in the result, but the code should be oblivious to any template argument coming after that and should not care whether it's a type or a value.如果结果容器有不同的数据类型,我们需要知道容器操作的类型,所以对任何容器的唯一要求是模板的第一个参数需要是输入类型,以便可以用不同的类型替换结果中的输出类型,但代码应该忽略其后的任何模板参数,并且不应该关心它是类型还是值。

Your interesting construct has two levels with variadic templates.您有趣的构造有两个带有可变参数模板的级别。

  • An outer variadic template parameter list TemplateP & Sizes for a function template函数模板的外部可变参数模板参数列表TemplateP & Sizes
  • An inner parameter pack as the template parameters of your template template parameter TemplateT , a class template一个内部参数包作为你的模板模板参数TemplateT模板参数,一个类模板

First, let's look at the inner TemplateT class: why can the ellipsis operator not not match something like TemplateT< int, 2 > ?首先,让我们看看内部TemplateT类:为什么省略号运算符不能匹配TemplateT< int, 2 > Well, the standard defines variadic templates in §14.5.3 as好吧,该标准在 §14.5.3 中将可变参数模板定义为

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 .其中在第一种情况下的模板参数组可以匹配的类型和在所述第二版本唯一值类型的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:因为标准在 §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).一个函数模板的模板参数包后面不能跟另一个模板参数,除非该模板参数可以从函数模板的参数类型列表中推导出来或者有一个默认参数(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 )此外,您可能需要一个从任意容器中提取值类型的元函数,而不仅仅是符合标准的( 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;
};

It would be awesome if we had such thing, as it would allow us to write a is_same_template trait in a breeze.如果我们有这样的东西会很棒,因为它可以让我们轻而易举地编写is_same_template特征。
Until then, we specialize all the way.在那之前,我们一直专注于专业。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM