用 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)要指定所用的枪的类型及单双手。它们之间的类型关系可用如下类图来表示,

class

射箭比赛中允许使用任意类型的弓,而不允许用枪;而射击比赛中所允许使用的枪要与单、双手保持一致。

以下代码片段展示了,在此例子中如何用 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

posted @ 2023-11-17 10:42  green-cnblogs  阅读(69)  评论(0编辑  收藏  举报