我已经看到了一些代码(https://stackoverflow.com/a/19209751/877329)使用递归“继承”到mangle模板参数包。这种结构背后的想法是什么?它是如何工作的?继承与参数模板包。有什么窍门?
回答
前言
虽然模板递归往往是必要的,没有什么是必然需要使用继承。这就是说,它已成为相当标准的使用模式:
template <typename T, typename = void>
struct has_trait : std::false_type {};
template <typename T>
struct has_trait<T, std::enable_if_t<some_predicate>> : std::true_type {};
也就是说,该标准还提供了方便的类型std::{true,false}_type
,因为他们有静态constexpr
value
成员。这种模式强烈表明递归继承是常见的并且受到鼓励。
递归继承
假设我要确定一个参数包包含T
类型的参数。要做到这一点,我要创建类型的实现struct
:
template <bool B, typename T, typename ... ARGS>
struct pack_with_type_impl;
非类型参数B
是“匹配”参数; T
是我们感兴趣匹配的参数; ARGS...
是参数包。
我们将使用此模板的特化来处理各种情况。
空参数包
让其中的参数组是空的接这个案子。在这种情况下,专业化的样子:
template <bool B, typename T>
struct pack_with_type_impl<B,T> : std::false_type {};
有明显的空参数包没有类型T
,使专业化的std::false_type
继承。
匹配参数包
现在让我们说,它已确定类型T
的参数不在参数包存在。在这种情况下,专业化的样子:
template <typename T, typename ... ARGS>
struct pack_with_type_impl<true, T, ARGS...> : std::true_type {};
的递推
现在是最有趣的部分。到目前为止,我们没有进行递归,因为上面的例子代表了终止条件:一个参数包是空的,所以没有什么可做的了。还有一场比赛已经找到了,所以没有什么可做的了。但让我们现在做的部分,其中还没有找到了一个匹配项。在这种情况下,我们会有这样的事情:
template <typename T, typename H, typename ... TAIL>
struct pack_with_type_impl<false, T, H, TAIL...> :
pack_with_type_impl<std::is_same<T,H>::value, T, TAIL...> {};
什么?在这种情况下,匹配是false
,所以它将从模板继承,只提供一个参数。也就是说,对应于参数包的头部的参数H
已被剥离,以便自己进行测试,并且如果需要将保留类型T
和TAIL...
以供将来处理。 std::is_same
功能只是检查类型是否相同。继承链继续前进,每次剥离头部,直到达到终止条件之一。
举个例子,假设我想检查一个参数包是否有类型int
,我提供的参数是:char,double,float
。继承链是这样的:
pack_with_type_impl<false,int,char,double,float>
==> pack_with_type_impl<false,int,double,float>
==> pack_with_type_impl<false,int,float>
==> pack_with_type_impl<false,int> # and since the parameter pack is empty now...
==> std::false_type
在另一方面,如果我提供的参数char,int,double
,继承链将是:
pack_with_type_impl<false,int,char,int,float>
==> pack_with_type_impl<false,int,int,float>
==> pack_with_type_impl<true,int,float> # and since the first argument is true
==> std::true_type
评论
有当然更优雅的方式来做到这一点。对于初学者来说,我可能会创建沿线的几个别名模板:
template <typename ... ARGS>
using has_int = pack_with_type_impl<false,int,ARGS...>;
,这样我可以打电话:
template <typename ... ARGS>
void increment_int(std::tuple<ARGS...>& tup) {
static_assert(has_int<ARGS...>::value,
"Can only call increment_int on tuples with an int type!");
...
}
但是这是在解释这个棘手的问题第一次尝试。
在通用代码中,您不能创建具有N个直接成员的类。然而,您可以创建一个带有直接成员和N-1
继承成员的类,专门为N==1
结束递归。
示例:假设您要创建一个类,使Foo<int, double, std::string>
包含3个成员函数void Foo::f(int); void Foo::f(int); void Foo::f(std::string)
。这不是直接可能的,因为N = 3。但是,您可以从Foo<double, std::string>
派生并添加一个成员void f(int)
。
template<typename HEAD, typename... Tail>
class Foo : public Foo<Tail...>
{
public: void f(HEAD h) { std::cout << h; }
};
template<typename HEAD>
class Foo<HEAD>
{
public: void f(HEAD h) { std::cout << h; }
};
- 1. C++模板专门与继承
- 2. 模板类参数继承
- 3. 模板继承:没有参数取决于模板参数
- 4. 继承与模板
- 5. 模板函数参数继承
- 6. 模板继承和可变参数
- 7. 有没有什么办法让所有的模板都继承母模板
- 8. 类具有可变参数模板成员函数继承
- 9. 什么时候使用模板与继承
- 10. 函数模板和私有继承
- 11. 具有模板函数的继承类
- 12. 模板继承C++
- 13. 模板和继承
- 14. Django模板继承
- 15. WPF模板继承
- 16. djangocms模板继承
- 17. javascript模板继承
- 18. 玉模板继承
- 19. Jinja2模板继承
- 20. 从具有多个参数的模板类继承时出错
- 21. 继承与类别有什么区别
- 22. 模式可以基于继承来专门化模板吗?
- 23. 具有模板参数的模板函数专门化
- 24. 有没有什么窍门禁止C宏被称为左值?
- 25. 混合模板/非模板继承分类和成员继承
- 26. 多继承与模板接口
- 27. JavaScript doesen't与Django模板继承工作
- 28. C++访问继承类的成员,其中继承的类是模板参数
- 29. 从模板基类继承构造函数而不重复模板参数?
- 30. 有什么窍门可以追加...... CSS吗?
你在问这种方法的动机吗?或者你问它是如何工作的?后者是 – KyleKnoepfel
。我在另一个话题上看到了类似的方法。 – user877329
@ user877329难以用词来解释:) – Arunmu