Tamir Vered Tamir Vered -4 years ago 75
C++ Question

Template syntax for classifying abstract classes

While browsing some code I have encountered the following template:

template<typename T>
class is_abstract
class No { };
class Yes { No no[3]; };

template<class U>
static No test(U (*)[1]); // not defined

template<class U>
static Yes test(...); // not defined

enum { result = (sizeof(test<T>(0)) == sizeof(Yes)) };

I understand that
will be invoked with the overload which returns
for non-abstract (
= true
) classes and the other overload otherwise, but why?

What is this
U (*)[1]
syntax in the method signature?

Answer Source

test<T>(0) will either call the first or the second overload of test. The second overload is quite standard (it is the fallback when the first overload is ill-formed).

Now, how to make the first declaration ill-formed for abstract class? Here are some possible solutions:

template<class U>
static No test(U); // (1)

template<class U>
static No test(U*); // (2)

template<class U>
static No test(U (*)[1]); // (3)

But there are problem with (1) and (2):

  1. You would need to call test<T>(T()), which would be ill-formed for any abstract class T (not only the declaration, but also the call), so SFINAE would not work (there would be no fallback since the code would be ill-formed "before" overload resolution);
  2. U* is always valid, even for an abstract class, so this is not OK;
  3. Is probably the simpler solution here since U (*)[1] do not require you to instantiate a T() during the call but the declaration is ill-formed for abstract type so the fallback to test(...) works as needed.

Note that since C++11, there is a std::is_abstract class defined by the standard.

Side note on is_* classes:

This is probably an old implementation, there are easier way to do this now (since C++11). Also note that using value instead of result would be much better here (would follow standard convention). A c++11 implementation following the standard's convention could be1:

template <class T>
std::false_type is_abstract_(T (*)[1]);

template <class T>
std::true_type is_abstract_(...);

template<typename T>
struct is_abstract: decltype(is_abstract_<T>(0)) { };

1 If you need to test if a class is abstract or not, use the standard std::is_abstract, but if you need to create your own is_* classes, you should follow this example (or similar ones) instead of the one you found.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download