Karel Petranek
Karel Petranek

Reputation: 15164

Detecting if a type can be derived from in C++

I have the following template class and a (global) variable of its type:

template <typename ClassT>
struct ClassTester : public ClassT {
    typedef ClassT type;
};

ClassTester<int> *aaa;  // No error here

I would expect a compilation error because int cannot be derived from, but this compiles fine under Visual C++ 2010.

If I remove the pointer, I get the expected compilation error (int cannot be derived from):

ClassTester<int> bbb; // Error here

I wanted to use this class for SFINAE testing whether the given type is a class that can be derived from:

template <typename T>
struct CanBeDerivedFrom  {

    template <typename C>
    static int test(ClassTester<T> *) { }

    template <typename>
    static char test(...) { }

    static const bool value = (sizeof(test<T>(0)) == sizeof(int));
};

This, however, always reports true, even for primitive types such as int because of the above reason. Is this an expected/valid behavior of C++?

Upvotes: 6

Views: 511

Answers (6)

Matthieu M.
Matthieu M.

Reputation: 299999

Unfortunately I think that this is actually impossible.

Many issues may prevent derivation (or at least, useful derivation), the addition of final to the standard being one.

For example, see this thread on the Clang mailing list where Howard Hinnant requires a compiler intrinsic to check whether the class is marked as final or not.

Upvotes: 2

iammilind
iammilind

Reputation: 70030

I think it's not possible entirely to get a class which is derivable through SFINAE (which includes also the cases of final class in C++11). The best thing which can be done is to have a SFINAE for finding if a type is a class and rely upon that.

template<typename T>
struct void_ { typedef void type; };

template<typename T, typename = void>
struct CanBeDerivedFrom {
  static const bool value = false;
};

template<typename T>
struct CanBeDerivedFrom<T, typename void_<int T::*>::type> {
  static const bool value = true;
};

This metaprogram will find if the given type is class/union or not. demo.

Upvotes: 1

Jeremy
Jeremy

Reputation: 834

I was going to suggest as visitor had. I don't believe declaring a pointer will instantiate the template, which is why it may compile. Try accessing a member using the template via the pointer, which would force the compiler to instantiate the template. Otherwise, I'm not too sure - but I know that you can't inherit an integer type.

So, the answer I suppose would be you don't need to, as the code probably won't compile in the event that you do try to instantiate a template class inheriting an integer type. I may be wrong, but I believe the only reason it is compiling is because creating a pointer type does not instantiate the template.

Upvotes: 1

andrea.marangoni
andrea.marangoni

Reputation: 1499

#include <typeinfo>

main()
{
 int i;
 int * pi;
 cout << int is:  << typeid(int).name() << endl;
 cout <<   i is: << typeid(i).name() << endl;
 cout <<  pi is:  << typeid(pi).name() << endl;
 cout << *pi is:  << typeid(*pi).name() << endl << endl;

}

it prints:


int
int
int*
int

as expected..

someone needs to be indipendent of other library...so boost library it s not a good answer..

Upvotes: 0

reder
reder

Reputation: 1108

Do not reinvent the wheel. Use boost::is_class boost reference manual

Those guys known better than you do.

Upvotes: 4

Srivathsa
Srivathsa

Reputation: 961

You can use RTTI(Run Time Type Information) to know what type the class belongs to , and if the class is of basic type you can tell that the class cannot be derived from.

Ex: if(typeid(T) == typeid(int) || typeid(T) == typeid(float)) { cout << "The class cannot be derived from"; }

You can add more types if you want , to the IF condition

Upvotes: 0

Related Questions