8

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)

Benji Mizrahi
  • 2,154
  • 2
  • 23
  • 38
  • 3
    Class does not need to have members to declare a pointer to member type. It is similar to not having a function not preventing from declaring a pointer to function type. – user7860670 Jun 21 '18 at 18:45

2 Answers2

9

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
}
rustyx
  • 80,671
  • 25
  • 200
  • 267
4

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)

It does not have to be int, as it just performs its role as a placeholder. You can replace that with double or char and see the same result coming.

It doesn't matter at all whether the given class type T does have a member function, because what IsClass tries to see is just that expression:

X Y::*

is well-formed.

This is just like you don't need an actual function definition (non-member function) merely to declare a type of pointer to that function as follows:

int main()
{
    // It doesn't matter whether there's a function int (*)(int, int, int) indeed because it's just merely a declaration
    using FuncP = int (*)(int, int, int);
    FuncP P;
}
Dean Seo
  • 5,486
  • 3
  • 30
  • 49