Workspace 6.21.5
Public Types | List of all members
TestIfDerivedFrom< Derived, Base > Class Template Reference

Define a constraints base class with a testable value. More...

#include <Workspace/Application/LanguageUtils/classconstraints.h>

Public Types

enum  { Is }
 

Detailed Description

template<typename Derived, typename Base>
class CSIRO::Application::TestIfDerivedFrom< Derived, Base >

Taken from "Extensible Templates: Via Inheritance or Traits?", Herb Sutter, C/C++ Users Journal, Feb 2002, pp28-38. Note that the merged version called "IsDerivedFrom" in his article does not appear to compile. The workaround for this is to break it up into two separate class templates, one containing the test function machinery and the other to contain just the "Is" enum.

This is a more complicated version of IsDerivedFrom, but serving a slightly different function. It provides the ability to determine at compile time whether a class is derived from another, but it does this through an enumerated data member. This data member can be queried at compile time and used as a template parameter. Hence, it allows you to supply different member functions for a class depending on whether one template parameter is derived from another or not. Consider this:

// General case where T is not derived from Base (call this class C1)
template<typename T, int>
class XImpl { ... }
// Specialised case where T *is* derived from Base (call this class C2)
template<typename T, int>
class XImpl<T,1> { ... }

The second class is a specialization for when the second template parameter is 1. We can now make use of this in a separate class:

template<typename T>
class X
{
XImpl<T, TestIfDerivedFrom<T,Base>::Is> impl;
}
@ X
Map the vector's X component. The Y and Z components are ignored.
Definition: vectormapping.h:47

The class contains a single data member which is used to access the class that really does the work. Now consider the following:

X<Base> x1;
X<Derived> x2;
X<Unrelated> x3;
// Use as follows
x1.impl.DoSomething;

The impl data member of x1 has the type

XImpl<Base, TestIfDerivedFrom<Base,Base>::Is>

which resolves to

XImpl<Base,1>

and therefore corresponds to class C2.

Similarly, the impl data member of x2 has the type

XImpl<Derived, TestIfDerivedFrom<Derived,Base>::Is>

and we know that "Derived" is a subclass of "Base", so this resolves to

XImpl<Derived,1>

and again corresponds to class C2.

Now look at the impl data member of x3. It has the type

XImpl<Unrelated, TestIfDerivedFrom<Unrelated,Base>::Is>

and we know that "Unrelated" is not derived from "Base". Therefore, this resolves to

XImpl<Unrelated,0>

which corresponds to class C1.

This shows how you can use TestIfDerivedFrom to take different actions depending on whether a class is derived from another or not.

As a final thought, changing impl to be a typedef instead of a data member may make the process a bit more transparent (this was not part of Sutter's original article):

template<typename T>
class X
{
typedef XImpl<T, TestIfDerivedFrom<T,Base>::Is> Impl;
}
X<Base>::Impl x1;
X<Derived>::Impl x2;
X<Unrelated>::Impl x3;
// Now we can use the variables directly. Eg
x1.DoSomething;

NOTE: I haven't tested this extension (yet!), but it should work just fine.

Member Enumeration Documentation

◆ anonymous enum

anonymous enum
Enumerator
Is