1

Assuming the following class hierarchy:

//file base.h
class IBase
{
public:
    virtual ~IBase() = default;

    //a static member identifying IBase (e.g. "iid.base")
    static const char* const IID;  //initialize in implementation file
//[...]
};//class IBase

class IDerived : public IBase
{
public:
    virtual ~IDerived() = default;

    //a static member identifying IDerived (e.g. "iid.derived")
    static const char* const IID;  //initialize in implementation file
//[...]
};//class IDerived

class IEvenMoreDerived : public IDerived
{
public:
    virtual ~IEvenMoreDerived() = default;

    //missing static const member IID!
//[...]
};//class IEvenMoreDerived

Every class in this hierarchy must have its own static const tChar* const iid which can be used to identify the object without instantiating it. In IEvenMoreDerived this information is missing.

Now, somewhere in the code, I have a function template where the IID is accessed:

//file main.cpp
#include "base.h"

template<typename T>
void queryIID()
{
    std::cout << T::IID << std::endl;
}

int main(int argc, char* argv[])
{
    queryIID<IBase>();             //prints "iid.base"
    queryIID<IDerived>();          //prints "iid.derived"
    queryIID<IEvenMoreDerived>();  //probably prints "iid.derived"
    return 0;
}

My intention is to get a compile time error when using queryIID<IEvenMoreDerived>(); as IEvenMoreDerived doesn't have a static member iid. I assume it might be possible using some template magic inside function template queryIID(), but everything I tried didn't solve the issue.

Some additional points:

  • Using constexpr char* instead of static const char* const unfortunately seems not possible as according to The MS VS2015 Feature Preview even in VS2015 it won't be supported.
  • After some digging I came across the C++ Templates FAQ. According to this, member hiding is a "feature" of class templates. I just couldn't figure out how I might use this to solve my problem. Furthermore I don't want the class hierarchy as presented above to be changed to class.
  • Among other questions, I found this somewhat similar question on SO, but it didn't entirely match my problem.
Community
  • 1
  • 1
PiJ
  • 151
  • 1
  • 12
  • Looks like a solution looking for a problem to me. If your IIDs are needed and you don't define them, the compiler and/or linker will miss them sooner or later. At best you will replace a genuine compiler/linker error with an über cryptic bunch of template errors. – kuroi neko Feb 26 '15 at 08:55
  • Can `typeid` help here? – sharptooth Feb 26 '15 at 08:56
  • You can't identify an OBJECT w/o instantiating it, by definition an object is the instantiation of a CLASS. – Rugruth Feb 26 '15 at 09:11
  • 1
    @kuroineko: Curently, with the OP's code, `queryIID();` prints `"iid.derived"`, so no linker/compiler error, just an unwanted behavior. – Jarod42 Feb 26 '15 at 10:12
  • @Jarod42 yep, sorry. I responded too quickly. – kuroi neko Feb 26 '15 at 11:19

1 Answers1

1

As a workaround, you may delegate Id in an external structure, something like:

template <typename T>
struct IdName
{
    static const char* const IID;
};

// instead of `const char* const IBase::IID = "IBase";`
// you have to use the following
template <> const char* const IdName<IBase>::IID = "IBase";
// Same for IDerived
template <> const char* const IdName<IDerived>::IID = "IDerived";

And you got linking error when trying to use IdName<IEvenMoreDerived>::IID.

Live example

Jarod42
  • 203,559
  • 14
  • 181
  • 302