C ++: как требовать, чтобы один тип шаблона был производным от другого - PullRequest
14 голосов
/ 13 апреля 2010

В операторе сравнения:

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

Можно ли как-нибудь принудительно установить, что R1 и R2 должны иметь отношение супертипа или подтипа? То есть я хотел бы разрешить, чтобы R1 был получен из R2, или R2 был получен из R1, но запретить сравнение, если R1 и R2 являются несвязанными типами.

Ответы [ 6 ]

24 голосов
/ 13 апреля 2010

Черта, которую вы хотите, может выглядеть так:

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);
};

Тогда вам просто нужен какой-то статический запрос:

// really basic
template <bool>
struct static_assert;

template <>
struct static_assert<true> {}; // only true is defined

#define STATIC_ASSERT(x) static_assert<(x)>()

Затем сложите их вместе:

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;
}

Если один не является производным от другого, функция не будет компилироваться. (Ваша ошибка будет похожа на «static_assert<false> не определено» и будет указывать на эту строку.)

21 голосов
/ 13 апреля 2010

Вы можете использовать типизацию boost ( is_base_of ) и boost * 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;
}

С другой стороны, почему использование оператора == имеет большее значение для типов того же дерева наследования? Не придется ли использовать двойную диспетчеризацию для достижения значимых результатов?

3 голосов
/ 12 сентября 2012

В этом посте я рассматриваю проблему проверки, точно ли тип соответствует другому, это не совсем то, о чем просят, но это проще, и я надеюсь, что это поможет понять применяемые приемы шаблона.

Как и в Boost, для этой задачи могут быть приняты специализации шаблонов, фактически вы можете определить структуру шаблона, содержащую операции для данного типа, и использовать для этих операций вложенные структуры шаблона. В нашем случае:

// 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; };
};

Определение макроса позволяет легко использовать его:

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

следующим образом:

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;
}
1 голос
/ 13 апреля 2010

Должен признать, что я не вижу мотивов, особенно если это требует написания дополнительных пакетов кода поддержки. Для вашего оператора:

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

для компиляции без предупреждения, оба типа параметров шаблона должны быть в качестве параметров для шаблона Manager, и эти типы должны иметь закрытые члены (я предполагаю, что p1 & p2 должны быть m1 & m2), называемые internal_field. Учитывая эти ограничения, какова вероятность того, что эта шаблонная функция может быть случайно вызвана с неправильным типом (ами)?

1 голос
/ 13 апреля 2010
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;
}

Кредиты идут на http://www2.research.att.com/~bs/bs_faq2.html#constraints

1 голос
/ 13 апреля 2010

Если бы concept s было бы включено в C ++ 0x, вы могли бы использовать их с компилятором, который их реализует (например, gcc).

Поскольку это не так, единственной доступной альтернативой для выполнения того, что вы хотите, кажется, является библиотека Boost Concept Check .

...