Помогите с чертами типа - PullRequest
5 голосов
/ 14 января 2010

Предположим, у нас есть следующий класс шаблона

template<typename T> class Wrap { /* ... */ };

Мы не можем изменить Wrap. Это важно.

Пусть есть классы, производные от Wrap<T>. Например,

class NewInt  : public Wrap<int>     { /* ... */ };
class MyClass : public Wrap<myclass> { /* ... */ };
class Foo     : public Wrap<Bar>     { /* ... */ };

Мы не можем изменить эти классы тоже. Все классы выше являются третьими лицами. Они не мои.

Мне нужно следующее время компиляции type_traits:

template<class T>
struct is_derived_from_Wrap { 
     static const bool value = /* */;
};

Что мне нужно?

assert(is_derived_from_Wrap<Int>::value == true);  // Indeed I need static assert
assert(is_derived_from_Wrap<MyClass>::value == true);
assert(is_derived_from_Wrap<char>::value == false);
struct X {};
assert(is_derived_from_Wrap<X>::value == false);

Ответы [ 3 ]

9 голосов
/ 14 января 2010

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

template<typename T> class Wrap { };

struct myclass {};
struct X {};

class Int     : public Wrap<int>     { /* ... */ };
class MyClass : public Wrap<myclass> { /* ... */ };

template< typename X >
struct is_derived_from_Wrap
{
  struct true_type { char _[1]; };
  struct false_type { char _[2]; };

  template< typename U >
    static true_type test_sfinae( Wrap<U> * w);
  static false_type test_sfinae( ... );

  enum { value = sizeof( test_sfinae( (X*)(0) ) )==sizeof(true_type) };
};


#include <iostream>
#define test(X,Y) std::cout<<( #X " == " #Y )<<"  : "<<( (X)?"true":"false") <<std::endl;

int main()
{
  test(is_derived_from_Wrap <Int>::value, true);
  test(is_derived_from_Wrap <MyClass>::value, true);
  test(is_derived_from_Wrap <char>::value, false);
  test(is_derived_from_Wrap <X>::value, false);
}

Это дает ожидаемый результат

is_derived_from_Wrap <Int>::value == true  : true
is_derived_from_Wrap <MyClass>::value == true  : true
is_derived_from_Wrap <char>::value == false  : false
is_derived_from_Wrap <X>::value == false  : false

Есть пара ошибок с моим кодом. Он также вернет true, если тип является Wrap.

assert(  is_derived_from_Wrap< Wrap<char> >::value == 1 );

Возможно, это можно исправить, используя немного больше магии SFINAE, если необходимо.

Будет возвращено значение false, если деривация не является публичной деривацией (т.е. является частной или защищенной)

struct Evil : private Wrap<T> { };
assert( is_derived_from_Wrap<Evil>::value == 0 );

Я подозреваю, что это не может быть исправлено. (Но я могу ошибаться). Но я подозреваю, что публичного наследства достаточно.

0 голосов
/ 14 января 2010

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

  1. Y может быть неявно преобразовано в X
  2. X и Y не одного типа

Андрей Александреску объясняет, как это сделать (наряду со многими другими приемами шаблонов) в своей книге "Современный дизайн C ++".

Вы можете найти код, который решает вашу проблему, либо в библиотеке Loki , либо в реализации uSTL , написанной Александреску.

0 голосов
/ 14 января 2010

Следующее определяет, является ли что-то оберткой:

template<class T>
struct is_Wrap { static const bool value = false; };

template<typename T> 
struct is_Wrap< Wrap<T> > { static const bool value = true; };

Поскольку деривация - это отношение Is-A, все, что происходит от Wrap<T>, также является Wrap<T> и должно быть найдено по этому.

...