C++: How to require that one template type is derived from the other

前端 未结 6 936
北恋
北恋 2020-11-30 04:42

In a comparison operator:

template
bool operator==(Manager m1, Manager m2) {
    return m1.internal_field == m2         


        
相关标签:
6条回答
  • 2020-11-30 04:45

    A trait you want might look like this:

    template <typename B, typename D>
    struct is_base_of // check if B is a base of D
    {
        typedef char yes[1];
        typedef char no[2];
    
        static yes& test(B*);
        static no& test(...);
    
        static D* get(void);
    
        static const bool value = sizeof(test(get()) == sizeof(yes);
    };
    

    Then you just need a static assert of some sort:

    // really basic
    template <bool>
    struct static_assert;
    
    template <>
    struct static_assert<true> {}; // only true is defined
    
    #define STATIC_ASSERT(x) static_assert<(x)>()
    

    Then put the two together:

    template<class R1, class R2>
    bool operator==(Manager<R1> m1, Manager<R2> m2)
    {
        STATIC_ASSERT(is_base_of<R1, R2>::value || is_base_of<R2, R1>::value);
    
        return p1.internal_field == p2.internal_field;
    }
    

    If one does not derive from the other, the function will not compile. (Your error will be similar to "static_assert<false> not defined", and it will point to that line.)

    0 讨论(0)
  • 2020-11-30 04:51

    I must admit, I don't see the motivation behind this, particularly if it requires writing shedloads of supporting code. For your operator:

    template<class R1, class R2>
    bool operator==(Manager<R1> m1, Manager<R2> m2) {
        return p1.internal_field == p2.internal_field;
    }
    

    to compile without a warning, both template parameter types must be capable of being parameters to the Manager template, and those types must have private members (I assume p1 & p2 should be m1 & m2) called internal_field. Given those constraints, what is the chance that this template function can be called by accident on the wrong type(s)?

    0 讨论(0)
  • 2020-11-30 05:04

    In this post I'm considering the problem of checking if a type exactly matches another, it's not exactly what is asked for, but it is simpler and I hope it can help to understand the applied template tricks.

    As done in boost, template specializations can be adopted for that task, in fact you can define a template struct containing operations on a given type, and use nested template structs for that operations. In our case:

    // Working on a specific type:
    template <typename T1>
    struct is_type {
        // For all types T2!=T1 produce false:
        template <typename T2>
        struct same_of { static const bool value = false; };
        // Specialization for type T2==T1 producing true:
        template <>
        struct same_of<T1> { static const bool value = true; };
    };
    

    Defining a macro allows to use it easily:

    #define is_type_same(T1,T2) (is_type<T1>::same_of<T2>::value)
    

    as follows:

    template<class R1, class R2>
    bool operator==(Manager<R1> m1, Manager<R2> m2) {
        return is_type_same(R1,R2) && m1.internal_field == m2.internal_field;
    }
    
    0 讨论(0)
  • 2020-11-30 05:04

    If concepts would have been included in C++0x you might have been able to use them with a compiler that implement them (like gcc).

    As it's not the case, the only alternative currently available to do what you want seem to be the Boost Concept Check library.

    0 讨论(0)
  • 2020-11-30 05:06
    template<class T, class B> struct Derived_from {
     static void constraints(T* p) { B* pb = p; }
     Derived_from() { void(*p)(T*) = constraints; }
    };
    
    template<class R2, class R1>
    bool test(R1& r1) {
     Derived_from<R1,R2>(); // accept if R1 is derived from R2
     return false;
    }
    
    class Base {
    public:
     virtual ~Base() { }
    };
    
    class Derived : public Base {
    
    };
    
    class Other {
    
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
     Derived d;
     Other o;
    
     test<Base>(d); // OK
     test<Base>(o); // Fails in VC++ 2005
    
     return 0;
    }
    

    Credits go to http://www2.research.att.com/~bs/bs_faq2.html#constraints

    0 讨论(0)
  • 2020-11-30 05:07

    You can use boost's typetraits (is_base_of), and boost's enable_if.

    #include <boost/type_traits.hpp>
    #include <boost/utility/enable_if.hpp>
    
    template <class R1, class R2>
    struct has_derived_base_relationship :
        boost::integral_constant<
            bool, boost::is_base_of<R1, R2>::value || boost::is_base_of<R2, R1>::value 
        >
    {};
    
    template<class R1, class R2>
    typename boost::enable_if<has_derived_base_relationship<R1, R2>, bool>::type 
    operator==(Manager<R1> m1, Manager<R2> m2) {
        return p1.internal_field == p2.internal_field;
    }
    

    On the other hand, why would operator== usage have more value with types of the same inheritance tree? Wouldn't it have to use double dispatch to achieve meaningful results?

    0 讨论(0)
提交回复
热议问题