简体   繁体   中英

Determining if a type is derived from a CRTP base class

I'm trying to create an is_foo function, that I can then use with enable_if , to determine if a type is derived from a certain CRTP base class. The code below is my attempt at implementing the is_foo function, but it doesn't actually work. Could someone tell me what I need to change to fix it?

Thanks.

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

Add a typedef to the Foo base:

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

Implement a has_field check:

BOOST_MPL_HAS_XXX(is_crtp)

Implement your metafunction:

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

This is the only way I can think of that will correctly catch grandchildren. It's prone to false positives though so you'll want to pick your names to be too obnoxious to be accidentally used elsewhere. Your other option would be to implement your metafunction in terms of is_base_of:

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

This of course won't catch grandchildren.

You could do it like this:

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

But you need to do the static_assert for each interface function you've defined. It's possible to use enable_if to make it more general:

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

This technique brings the disadvantage, that you have to deal with confusing compiler-errors, when 'foo' isn't implemented in 'derived'.

Let me think about that. Maybe I have some better solution for this case soon ;)

EDIT: Okay, I would prefer the following solution:

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

Please note, that you need to define the default constructor for base-class in 'protected' section. If you don't do this, a call to a member function could raise an access-violation, because it might access memory, which wasn't allocated. It's not safe to declare objects of type 'base'.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM