Определение, является ли тип производным от базового класса CRTP - PullRequest
3 голосов
/ 22 февраля 2011

Я пытаюсь создать функцию is_foo, которую затем могу использовать с enable_if, чтобы определить, является ли тип производным от определенного базового класса CRTP.Приведенный ниже код является моей попыткой реализовать функцию is_foo, но на самом деле она не работает.Может кто-нибудь сказать мне, что мне нужно изменить, чтобы это исправить?

Спасибо.

#include <iostream>
#include <type_traits>
#include <functional>

using namespace std;

template <class Underlying, class Extra>
struct Foo
{
    int foo() const { return static_cast<const Underlying*>(this)->foo(); }
};

template<class T>
struct Bar : Foo<Bar<T>, T>
{
    int foo() const { return 42; }
};

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

template<class Underlying, class Extra>
struct is_foo<Foo<Underlying, Extra> > { static const bool value = true; };

template<class T>
void test(const T &t)
{
    cout << boolalpha << is_foo<T>::value << endl;
}

int main()
{
    Bar<int> b;
    test(b);
}

Ответы [ 2 ]

3 голосов
/ 22 февраля 2011

Добавление typedef к базе Foo:

template < typename Derived >
struct crtp
{
  ...
  typedef int is_crtp;
};

Реализация проверки has_field:

BOOST_MPL_HAS_XXX(is_crtp)

Реализация вашей метафункции:

template < typename T >
struct is_crtp_derived : has_is_crtp<T> {};

Этоединственный способ, которым я могу думать о том, что правильно поймать внуков.Однако он склонен к ложным срабатываниям, поэтому вы захотите выбрать свои имена, чтобы они были слишком неприятными, чтобы их случайно использовали в других местах.Другой вариант - реализовать метафункцию в терминах is_base_of:

template < typename T >
struct is_crtp_derived : std::is_base_of< crtp<T>, T> {};

Это, конечно, не поймает внуков.

0 голосов
/ 19 апреля 2011

Вы можете сделать это следующим образом:

typedef char (&yes)[1];
typedef char (&no )[2];


template<class container>
struct class_helper
{
    template<typename member> static no  has_implemented(member);
    template<typename member> static yes has_implemented(member container::*);  
};


template<class derived>
class base
{
protected:
    base() {}

public:
    void foo() 
    {
        static_assert(
            sizeof(class_helper<derived>::has_implemented(&derived::foo)) == sizeof(yes),
            "derived::foo not implemented"
        );
        static_cast<derived*>(this)->foo();
    }
};

Но вам нужно выполнить static_assert для каждой определенной вами интерфейсной функции.Можно использовать enable_if, чтобы сделать его более общим:

static_cast<typename enable_if_c<
    sizeof(class_helper<derived>::has_member(&derived::foo)) == sizeof(yes), derived
>::type*>(this)->foo();

Этот метод имеет недостаток, заключающийся в том, что вам приходится сталкиваться с ошибками компилятора, когда «foo» не реализован в «производном».

Дайте мне подумать об этом.Может быть, у меня есть лучшее решение для этого случая в ближайшее время;)

РЕДАКТИРОВАТЬ: Хорошо, я бы предпочел следующее решение:

template<class derived>
class base
{
    template<std::size_t n> struct test {
        static_assert(n != sizeof(no), "derived doesn't implement used interface method");
    };

protected:
    base() {}

public:
    void bar()
    {        
        static_cast<derived*>(this)->bar();
        test<sizeof(class_helper<derived>::has_implemented(&derived::bar))>();
    }
};

Обратите внимание, что вам нужно определить конструктор по умолчанию длябазовый класс в «защищенном» разделе.Если вы этого не сделаете, вызов функции-члена может вызвать нарушение прав доступа, так как он может получить доступ к памяти, которая не была выделена.Не безопасно объявлять объекты типа «base».

...