Help with type traits
Suppose we have the following template class
template<typename T> class Wrap { /* ... */ };
We can not change Wrap
. It is important.
Let there are classes derived from Wrap<T>
. For example,
class NewInt : public Wrap<int> { /* ... */ };
class MyClass : public Wrap<myclass> { /* ... */ };
class Foo : public Wrap<Bar> { /* ... */ };
We can not change these classes too. All classes above is 3rd party. They are not mine.
I need the following compile time type_traits
:
template<class T>
struct is_derived_from_Wrap {
static const bool value = /* */;
};
What do I need?
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);
You can do this using SFINAE but its kind of magical if you dont know whats going on...
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);
}
This gives the expected output
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
There are a couple of gotchas with my code. It will also return true if the type is a Wrap.
assert( is_derived_from_Wrap< Wrap<char> >::value == 1 );
This can probably be fixed using a bit more SFINAE magic if needed.
It will return false if the derivation is not a public derivation (i.e is private or protected )
struct Evil : private Wrap<T> { };
assert( is_derived_from_Wrap<Evil>::value == 0 );
I suspect this can't be fixed. (But I may be wrong ). But I suspect public inheritance is enough.
You need to do some fairly involved template metaprogramming to determine if one class X is derived from another Y, in the general case. Basically X derives from Y if:
- Y can be implicitly converted to X
- X and Y are not the same type
Andrei Alexandrescu explains how to do this (along with many other template tricks) in his book "Modern C++ Design".
You can find code which solves your problem either in the Loki library, or the uSTL implementation, both written by Alexandrescu.
The following determines whether something is a wrap:
template<class T>
struct is_Wrap { static const bool value = false; };
template<typename T>
struct is_Wrap< Wrap<T> > { static const bool value = true; };
Since derivation is an Is-A relationship, everything derived from Wrap<T>
also is a Wrap<T>
and should be found by this.
精彩评论