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 ofIsClass<T>
exploits the property (and picksint
arbitrarily for typeX
)
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
)
An object's class defines how the object is implemented. The class defines object's internal state and the implementation of its operations. In contrast, an object's type only refers to its interface - a set of requests to which it can respond.
IsSubclassOf. Which indicates that Derived is a subclass of Base , but that Base is (obviously) not a subclass of itself.
Use the typeof operator to check the type of a variable in TypeScript, e.g. if (typeof myVar === 'string') {} . The typeof operator returns a string that indicates the type of the value and can be used as a type guard in TypeScript.
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 ofpmd
is well-formed even thoughX
has no members of typedouble
. Similarly, the declaration ofpmc
is well-formed even thoughY
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
}
What I don't understand is why picking
int
asX
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With