Search code examples
c++templatessfinaetype-traitspartial-specialization

Determining whether a type is a class type?


In Chapter 19.8.4 of the book "C++ Templates - The Complete Guide - Second Edition", the authors show how one can determine if a type is a class type in compile-time:

#include <iostream>
#include <type_traits>
using namespace std;

template <typename T, typename = void_t<>>
struct IsClass : false_type { };

template <typename T>
struct IsClass<T, void_t<int T::*>> : true_type { };

int main()
{
    struct S { };
    cout << IsClass<S>::value; // prints 1
}

This paragraph explains how the partial specialization detects a class type:

... only class types can be used as the basis of pointer-to-member types. That is, in a type construct of the form X Y::*, Y can only be a class type. The following formulation of IsClass<T> exploits the property (and picks int arbitrarily for type X)

What I don't understand is why picking int as X works, even if we test IsClass<> with a struct S that has no members at all (It also works for class types having a member other than int)


Solution

  • In short, it's because the standard says so.

    According to [dcl.mptr]/2 (I'm including only the relevant parts here):

    [ Example:

    struct X {
      int a;
    };
    struct Y;
    
    double X::* pmd;
    char Y::* pmc;
    

    . . .
    The declaration of pmd is well-formed even though X has no members of type double. Similarly, the declaration of pmc is well-formed even though Y is an incomplete type.
    . . .

    Basically, as long as a type S is known to be of class type, the construct S::* is well-formed.

    So you could even have this:

    int main()
    {
        struct S;
        cout << IsClass<S>::value; // still prints 1
    }