用 Type Traits 约束模板类型参数
本文描述了如何运用 type traits 来约束模板的类型参数,要求参数类型满足某种条件,例如,参数类型只能是某种类型的派生类。
◆ 目的
如何能够约束模板的类型参数,在满足某种条件下模板才能成功地具体化?
◆ 解法
可以运用 type traits 来约束模板的类型参数。其要点在于用 typename 和 std::enable_if 为模板定义额外的类型参数或非类型参数,用不同的 type traits 模板在编译期间检查某个类型参数是否满足条件(更多 traits 请查阅 cppreference.com)。如果类型参数满足条件,则编译成功;如果不满足,则编译失败。
◆ 示例
考虑以下例子:目标运动中包括射箭和射击。射箭比赛用的弓(Bow),常有反曲弓(Recurve Bow)和复合弓(Compound Bow);射击比赛用的枪(Gun)常见有气手枪(Pistol)和气步枪(Rifle)。弓和气步枪需用双手(Two_Handed)控制,气手枪只需要单手(One_Handed)控制。射箭比赛(Archery_Game)要指定所用的弓的类型,射击比赛(Shooting_Game)要指定所用的枪的类型及单双手。它们之间的类型关系可用如下类图来表示,
射箭比赛中允许使用任意类型的弓,而不允许用枪;而射击比赛中所允许使用的枪要与单、双手保持一致。
以下代码片段展示了,在此例子中如何用 type traits 来约束模板的类型参数。
template
<
class _B,
class = typename std::enable_if<std::is_base_of<Bow, _B>::value>::type
> // #1
class Archery_Game
{
...
};
template
<
class _G,
class _C,
typename std::enable_if<std::is_base_of<Gun, _G>::value, int>::type = 0,
typename std::enable_if<std::is_base_of<Control, _C>::value, bool>::type = false,
class = typename std::enable_if<std::is_same<_G, typename std::conditional<std::is_same<_C, One_Handed>::value, Pistol, Rifle>::type>::value>::type
> // #2
class Shooting_Game
{
...
};
Archery_Game 模板类的参数(#1)中,_B 代表弓箭类型。结合使用 typename 和 std::enable_if 为匿名类型参数提供默认类型。std::is_base_of<> 在其中确保 _B 是 Bow 类的子类。Shooting_Game 模板类的参数(#2)中,_G 代表枪类型,_C 代表单手或双手控制。结合使用 typename 和 std::enable_if 分别定义了 int 和 bool 两个非类型参数并提供了默认值,以及一个匿名类型参数的默认类型(此处展示了两种不同的做法,不是必须的)。
int main()
{
Archery_Game<Recurve_Bow> ag; // OK
//Archery_Game<Gun> ag; // NG
ag.begin();
ag.end();
Shooting_Game<Pistol, One_Handed> sg1; // OK
//Shooting_Game<Pistol, Two_Handed> sg1; // NG
sg1.begin();
sg1.end();
Shooting_Game<Rifle, Two_Handed> sg2; // OK
//Shooting_Game<Rifle, One_Handed> sg2; // NG
sg2.begin();
sg2.end();
return 0;
}
此段代码中包括了正确和错误的两类用例。
◆ 验证
正确的用例可以编译(-std=c++11)成功,而错误的用例会编译失败并报告如下的错误信息,
error: failed requirement
'std::is_base_of<Bow, Gun>::value'; 'enable_if' cannot be used to disable this declaration
class = typename std::enable_if<std::is_base_of<Bow, _B>::value>::type
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using_type_traits_to_restrain_template_type_parameter.cpp:116:5: note: in instantiation of default
argument for 'Archery_Game<Gun>' required here
Archery_Game<Gun> ag;
^~~~~~~~~~~~~~~~~
error: failed requirement
'std::is_same<Pistol, typename std::conditional<std::is_same<Two_Handed, One_Handed>::value,
Pistol, Rifle>::type>::value'; 'enable_if' cannot be used to disable this declaration
...std::is_same<_G, typename std::conditional<std::is_same<_C, One_Handed>::value, Pistol, Rifle>::type>::value...
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using_type_traits_to_restrain_template_type_parameter.cpp:121:5: note: in instantiation of default
argument for 'Shooting_Game<Pistol, Two_Handed, 0, false>' required here
Shooting_Game<Pistol, Two_Handed> sg1;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: failed requirement
'std::is_same<Rifle, typename std::conditional<std::is_same<One_Handed, One_Handed>::value,
Pistol, Rifle>::type>::value'; 'enable_if' cannot be used to disable this declaration
...std::is_same<_G, typename std::conditional<std::is_same<_C, One_Handed>::value, Pistol, Rifle>::type>::value...
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using_type_traits_to_restrain_template_type_parameter.cpp:126:5: note: in instantiation of default
argument for 'Shooting_Game<Rifle, One_Handed, 0, false>' required here
Shooting_Game<Rifle, One_Handed> sg2;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 errors generated.
◆ 最后
完整的代码请参考 [gitee] cnblogs/17835563 。
受限于作者的水平,读者如发现有任何错误或有疑问之处,请追加评论或发邮件联系 green-pi@qq.com。作者将在收到意见后的第一时间里予以回复。 本文来自博客园,作者:green-cnblogs,转载请注明原文链接:https://www.cnblogs.com/green-cnblogs/p/17835563.html 谢谢!