Tagging objects using enums via template-template parameters

爷,独闯天下 提交于 2019-12-25 16:53:16

问题


I would like to use an enum argument of a template, to restrict a second argument, a class, to in turn taking an member of the enum as an argument as it's templated parameter. In code, I would expect this to look like:

CObject<EObjectTag, CSubObject<EObjectTag::CAT_A>> cObject;

this should work, however:

CObject<EObjectTag, CSubObject<ENotAnObjectTag::CAT_OTHER>> cObject;

should fail as ENotAnObjectTag::CAT_OTHER is not a element of EObjectTag.

My implementation (attempt) of this, is as follows and bombs out during compilation (on gcc version 4.9.2 (Ubuntu 4.9.2-10ubuntu13)) with the error message:

source.cc:16:45: error: ‘SUBOBJECT_TAG’ was not declared in this scope struct CObject>

#include <iostream>
#include <typeinfo>

enum class EObjectTag {CAT_A, CAT_B, CAT_OTHER};

// CSubObject
template<class OBJECT_TAG_T, OBJECT_TAG_T OBJECT_TAG>
struct CSubObject { OBJECT_TAG_T m_tTag = OBJECT_TAG; };

// CObject - Forward declaration
template <class SUBOBJECT_TAG_T, template <SUBOBJECT_TAG_T SUBOBJECT_TAG> class SUBOBJECT_T>
struct CObject;

// CObject - Specialization
template <class SUBOBJECT_TAG_T, template <SUBOBJECT_TAG_T SUBOBJECT_TAG> class SUBOBJECT_T>
struct CObject<SUBOBJECT_T<SUBOBJECT_TAG_T, SUBOBJECT_TAG>>
{
   public:
      SUBOBJECT_T<SUBOBJECT_TAG_T, SUBOBJECT_TAG> m_cSubObject;
};

int main() {
   // The aim is that the second object only accepts a tag that 
   // belongs to EObjectTag
   CObject<EObjectTag, CSubObject<EObjectTag::CAT_A>> cObject;

   return 0;
}

The final use case for this involves replacing CSubObject with CObject so that we can use recursion to define a hierarchy of tagged objects, which also requires the use of variadic templates to have multiple objects at the same level. For example:

/* EBase, */
CObject</*EBase::BASE,*/ EObject,
    CObject<EObject::INIT, EInitObject,
        CObject<EInitObject::INIT_FOO>,
        CObject<EInitObject::INIT_BAR>,
    >,
    CObject<EObject::COUNT, ECountObject,
        CObject<ECountObject::COUNT_FOO>,
        CObject<ECountObject::COUNT_BAR>,
    >,
> cMyObjectHierarchy;

The commented out references to EBase (an enum internal to the library) are there to keep the template parameters of CObject consistent, I would plan (if possible) to do this automatically via template specialization or default arguments.

My goals of specifying this hierarchy of objects would in addition include:

  1. Avoid forcing the user of this library to define additional classes or structs in their program
  2. Leverage compile time checking via the templating of CObject with an enum, whose functions in turn use that enum as an argument to a set of functions common to all CObjects

回答1:


I made some changes to make it compile. Although I'm not 100% sure if this actually does what you want it to do; I agree with most of what Yakk sais in his answer.

Note: the following will not compile because I deliberately tried to mix a type of one enum with a value of another enum to verify that it indeed triggers a compile-time error, which I think is sort of what you asked for.

#include <iostream>
#include <typeinfo>

enum class EObjectTag {CAT_A, CAT_B, CAT_OTHER};
enum class FObjectTag {DOG_A, DOG_B, DOG_OTHER};

// CSubObject
template<typename OBJECT_TAG_T, OBJECT_TAG_T OBJECT_TAG>
struct CSubObject { OBJECT_TAG_T m_tTag = OBJECT_TAG; };

// CObject - Specialization
template <class SUBOBJECT_TAG_T, SUBOBJECT_TAG_T SUBOBJECT_TAG, template <typename TYPE_T, TYPE_T TYPE> class SUBOBJECT_T>
struct CObject
{
   public:
      SUBOBJECT_T<SUBOBJECT_TAG_T, SUBOBJECT_TAG> m_cSubObject;
};

int main() {
   // The aim is that the second object only accepts a tag that 
   // belongs to EObjectTag
   CObject<EObjectTag, EObjectTag::CAT_A, CSubObject> cObject1;
   CObject<EObjectTag, FObjectTag::DOG_B, CSubObject> cObject2;

   return 0;
}



回答2:


An argument of template <SUBOBJECT_TAG_T SUBOBJECT_TAG> class SUBOBJECT_T is a template, not an instance of a template. CSubObject<blah> cannot match the kind template<...>class, because CSubObject<blah> is a type that is generated by the template, not a template. template<...>class parameters are templates, not types.

In addition, CSubObject is of kind template<class T, T> class, not template<SUBOBJECT_TAG_T>class. It takes two arguments, the first a type, the second a constant of that type: the kind template<SUBOBJECT_TAG_T>class is a template that takes one argument of type SUBOJECT_TAG_T. These are unrelated kinds of template.

Second, you seem to have issues with template specializations. Template specializations are pattern-matching of your primary specialization. They are not "new overloads". So arguments to CObject must first match the kinds of arguments the primary specialization of CObject expects. The stuff within template< blah > are used to pattern match the pattern in the CObject< blah > part of the specialization.

In general, it is conventional to use ALL CAPS only for macros, not for template arguments.

These are all problems with your code in your question. Your code lacks a clear problem statement or question, so the best I can do is describe fixes to your myriad of problems.


You have revised your question a bit.

template<class T, class U>
struct CObject;

template<class T, template<class Q, Q>class Z, T t>
struct CObject< T, Z<T, t> > {
};

live example.

Now, you still have to pass CSubObject<EObjectTag, EObjectTag::CAT_A> as the 2nd parameter.

You could also add a specialization:

template<class T, template<T>class Z, T t>
struct CObject< T, Z<t> > {
};

which, if you had a template<EObjectTag tag> struct Example;, you could CObject< EObjectTag, Example<EObjectTag::bob> > as well.



来源:https://stackoverflow.com/questions/33305628/tagging-objects-using-enums-via-template-template-parameters

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!