如何防止模板类多次派生? [英] How to prevent a template class from being derived more than once?

查看:93
本文介绍了如何防止模板类多次派生?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下模板类:

template<class I>
class T : public I
{
    // ...
};

对于给定的模板参数<$ c,此模板类需要派生一次(且仅一次) $ c>我。

class A : public T<U>  {};    // ok
class B : public T<V>  {};    // ok
class C : public T<U>  {};    // compile error

模板类 T 可以适应这种行为(当班级 A B U V 不能);但是, T 必须不了解派生类 A B C

Template class T can be adapted to achieve such a behavior (while classes A, B, U, V cannot); however, T must not have any knowledge about derived classes A, B, C.

有没有办法防止这样的模板类被多次派生?理想情况下,在这种情况下发出编译错误,或者至少是链接器错误。

Is there any way to prevent such a template class from being derived more than once? Ideally issuing a compilation error in such a case or, at least, a linker error.

推荐答案

如果是基类,这是可能的 T 知道其派生类的类型。这些知识可以通过CRTP或重载标记传递给它的构造函数。以下是后一种情况:

This is possible if the base class T knows the types of its derived classes. This knowledge can be passed by CRTP or by an overloading tag to its constructor. Here's the latter case:

template<class I>
class T : public I
{
protected:
    template< class Derived >
    T( Derived * ) {
        static_assert ( std::is_base_of< T, Derived >::value,
            "Be honest and pass the derived this to T::T." );

然后, T :: T(派生*)如果它有两个特化(具有不同的 Derived ),则需要做一些会导致问题的事情。朋友的功能非常棒。根据< T,Derived> 实例化辅助非成员类,并依赖于朋友函数 T 但不是派生

Then, T::T( Derived * ) needs to do something that will cause a problem if it has two specializations (with different Derived). Friend functions are great for that. Instantiate an auxiliary, non-member class depending on <T, Derived>, with a friend function that depends on T but not Derived.

        T_Derived_reservation< T, Derived >{};
    }
};

这是辅助类。 (它的定义应该在 T 之前。)首先,它需要一个基类来允许上的ADL T_Derived_reservation< T,Derived> 查找未提及 Derived 的签名。

Here's the auxiliary class. (Its definition should come before T.) First, it needs a base class to allow ADL on T_Derived_reservation< T, Derived > to find a signature that doesn't mention Derived.

template< typename T >
class T_reservation {
protected:
    // Make the friend visible to the derived class by ADL.
    friend void reserve_compile_time( T_reservation );

    // Double-check at runtime to catch conflicts between TUs.
    void reserve_runtime( std::type_info const & derived ) {
    #ifndef NDEBUG
        static std::type_info const & proper_derived = derived;
        assert ( derived == proper_derived &&
            "Illegal inheritance from T." );
    #endif
    }
};

template< typename T, typename Derived >
struct T_Derived_reservation
    : T_reservation< T > {
    T_Derived_reservation() {
        reserve_compile_time( * this );
        this->reserve_runtime( typeid( Derived ) );
    }

    /* Conflicting derived classes within the translation unit
       will cause a multiple-definition error on reserve_compile_time. */
    friend void reserve_compile_time( T_reservation< T > ) {}
};

当两个 .cpp 时出现链接错误会很好code> files声明了不同的不兼容派生类,但我无法阻止链接器合并内联函数。因此,断言将触发。 (您可以设法在标头中声明所有派生类,而不用担心断言触发。)

It would be nice to get a link error when two .cpp files declare different incompatible derived classes, but I can't prevent the linker from merging the inline functions. So, the assert will fire instead. (You can probably manage to declare all the derived classes in a header, and not worry about the assert firing.)

演示

您已编辑过说 T 无法知道其派生类型。好吧,在编译时你无能为力,因为这些信息根本不可用。如果 T 是多态的,那么您可以将动态类型视为派生类 A B ,但不在构造函数或析构函数中。如果派生类可靠地调用了其他一些函数,你可以挂钩:

You've edited to say that T cannot know its derived types. Well, there's nothing you can do at compile time, since that information is simply unavailable. If T is polymorphic, then you can observe the dynamic type to be the derived class A or B, but not in the constructor or destructor. If there's some other function reliably called by the derived class, you can hook into that:

template< typename I >
class T {
protected:
    virtual ~ T() = default;

    something_essential() {
    #ifndef NDEBUG
        static auto const & derived_type = typeid( * this );
        assert ( derived_type == typeid( * this ) &&
            "Illegal inheritance from T." );
    #endif
        // Do actual essential work.
    }
};

这篇关于如何防止模板类多次派生?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆