【问题标题】:How to ensure that the template parameter is a subtype of a desired type?如何确保模板参数是所需类型的子类型?
【发布时间】:2011-08-11 02:50:22
【问题描述】:

我有一个模板类,我想做的如下

  1. 确保仅当传递的模板参数是所需类型的子类型时才实例化对象
  2. 预先与代码的用户沟通模板参数必须满足什么

(1) 在某种意义上是自动处理的,如果传递的模板参数不支持该类使用的代码将无法编译的某些功能。但是这个错误可能会被检测到相当晚。我希望尽快进行检查。我还想要完成的是,很明显,传递的模板参数必须从我提供的基类型派生。

首先,这是被误导了吗?如果不是,我该怎么做? (请用最简单的方法,C++ 对我来说还是个新手)

感谢 stackoverflow,你确实加快了我的 C++ 学习速度。

【问题讨论】:

标签: c++ templates


【解决方案1】:

给定一些完整的类型MyBase,如果T 不是从MyBase 派生的,则以下将产生编译时错误:

#include <boost/type_traits/is_base_of.hpp>
#include <boost/static_assert.hpp>

template<typename T>
class Foo {
    BOOST_STATIC_ASSERT_MSG(
        (boost::is_base_of<MyBase, T>::value),
        "T must be a descendant of MyBase"
    );
    // Foo implementation as normal
};

如果您使用的是带有 TR1 的 C++03 编译器,则可以使用 std::tr1::is_base_of 而不是 boost::is_base_of;如果您使用的是 C++11 编译器,则可以使用 std::is_base_of 代替 boost::is_base_ofstatic_assert 关键字代替 BOOST_STATIC_ASSERT_MSG 宏:

#include <type_traits>

template<typename T>
class Foo {
    static_assert(
        std::is_base_of<MyBase, T>::value, 
        "T must be a descendant of MyBase"
    );
    // Foo implementation as normal
};

注意这将为私有和模糊派生类型产生 true_type,因此如果您真正需要的是对待 T as-a MyBase(在大多数情况下)。

文档链接:
Boost.StaticAssert
Boost.TypeTraits

【讨论】:

    【解决方案2】:

    来自“Modern C++ Design”,第 2.7 章(“Detecting Convertibility and Inheritance at Compile Time”):您可以使用sizeof 技巧:

    typedef char Small;
    class Big { char dummy[2]; };
    
    Small Test(Base1);
    Big Test(...);
    
    const bool isSubclassOfBase1 = sizeof(Test(Derived1())) == sizeof(Small);
    

    它利用sizeof(...) 可以确定表达式计算的类型的事实,并且由于返回值具有不同的大小,== 检查的计算结果为真或假。如果 Derived1 确实是 Base1 的基,则选择 Small Test(Base1); 重载并且 isSubclassOfBase1 将为真。

    由此扩展,您可以封装检查并执行静态断言以使其在编译时失败:

    #include <boost/static_assert.hpp>
    
    class A {};
    class B: public A {};
    class C {};
    
    template<class Base, class Derived>
    struct SubclassCheck
    {
        typedef char Yes;
        class No { char dummy[2]; };
    
        static Yes Test(Base);
        static No Test(...);
        enum {
            Value = (sizeof(Test(*(Derived*)NULL)) == sizeof(Yes))
        };
    };
    
    #define CHECK_DERIVES(b,d)\
        BOOST_STATIC_ASSERT((SubclassCheck<b,d>::Value));
    
    int
    main()
    {
        CHECK_DERIVES(A, B);
        // CHECK_DERIVES(A, C); // fails
    }
    

    您可以使用任何其他静态断言实现,不一定是 Boost 的。

    【讨论】:

    • 如果不相等,如何在编译时导致错误?不错的头像顺便说一句,喜欢那个游戏。
    • 这种方法的问题在于,它不能通过protected/private 继承。您可以查看this question for is_base_of,了解它是如何针对所有类型的继承实现的。
    【解决方案3】:

    是的,这是自动处理的,如果参数不支持您使用它执行的操作,则会导致编译错误。手动检查一个类型是否是另一种类型的子类型只能(AFAIK)在运行时完成,这比编译时晚。我不知道你说的那个错误被检测到是什么意思,编译时间是最早的。此外,如果每个人都检查他们的模板参数的类型,STL 就不能使用指针以及实际的基于类的迭代器,并且几乎没有那么灵活。

    至于让您的用户知道需求,只需在文档中提供即可。

    【讨论】:

    • @san 破坏了模板的许多应用程序和灵活性。为什么不直接接受Base* 并利用多态性而不是使用模板呢?这似乎更像你需要的。
    • 我不明白为什么它会破坏其他应用程序,(如果这很明显,请耐心等待,这一切对我来说都是新的)我不希望所有编译器都有这个强制检查。只想在一类中启用它(可选)。我想到的Base* 只是一个带有一些静态函数的类,我不想为实际对象分配内存。
    • @san 我的意思是它会破坏你的功能的其他应用,即在多少地方和方式可以应用它们。此外,您不必分配另一个对象来将子类指针转换为父类指针并使用多态性。你真的不应该为此使用模板。
    • @san 如果你能告诉我你的模板代码,我会告诉你如何使用多态性将它转换为非模板代码。
    • @san 哦,您只是在使用类对静态函数进行分组?您无法通过指针访问它们,因此您确实需要模板,但是它根本无法帮助您检查一个类是否从另一个类继承,因为静态函数不是继承的。也许您应该传递函数对象而不是使用模板来调用静态类函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-04
    • 1970-01-01
    • 2019-04-24
    相关资源
    最近更新 更多