通过模板模板参数使用枚举标记对象

Tagging objects using enums via template-template parameters

本文关键字:对象 枚举 参数      更新时间:2023-10-16

我想使用模板的枚举参数,来限制第二个参数,一个类,反过来接受枚举成员作为模板参数的参数。在代码中,我希望它看起来像:

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

这应该可以工作,但是:

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

应该失败,因为ENotAnObjectTag::CAT_OTHER不是EObjectTag的元素。

我的实现(尝试)如下,并在编译期间爆炸(在gcc版本4.9.2 (Ubuntu 4.9.2-10ubuntu13)),错误信息:

来源。cc:16:45:错误:' SUBOBJECT_TAG '未在此范围内声明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;
}

最后的用例涉及到用CObject替换CSubObject,这样我们就可以使用递归来定义标记对象的层次结构,这也需要使用可变模板来在同一层次上拥有多个对象。例如:

/* 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;

注释掉对EBase(库内部的枚举)的引用是为了保持CObject的模板参数一致,我计划(如果可能的话)通过模板特化或默认参数自动完成。

我指定这个对象层次结构的目标还包括:

    避免强制这个库的用户定义额外的类或结构
  1. 利用编译时间检查使用枚举模板化CObject,其函数反过来使用该枚举枚举作为所有对象通用的一组函数的参数

template <SUBOBJECT_TAG_T SUBOBJECT_TAG> class SUBOBJECT_T的参数是模板,而不是模板的实例。CSubObject<blah>不能匹配template<...>class类型,因为CSubObject<blah>是模板生成的类型,而不是模板。template<...>class参数是模板,不是类型。

另外,CSubObjecttemplate<class T, T> class的同类,而不是template<SUBOBJECT_TAG_T>class。它接受两个参数,第一个是类型,第二个是该类型的常量:template<SUBOBJECT_TAG_T>class类型是一个模板,它接受一个SUBOJECT_TAG_T类型的参数。这些是不相关的模板。

第二,你似乎在模板专门化方面有问题。模板专门化是主专门化的模式匹配。它们不是"新的超载"。因此,CObject的参数必须首先匹配CObject的主要专门化所期望的参数类型。template< blah >中的内容用于模式匹配专门化CObject< blah >部分中的模式。

一般来说,习惯上只对宏使用ALL CAPS,而对模板参数则不使用。

这些都是你问题中代码的问题。你的代码缺乏一个清晰的问题陈述或疑问,所以我能做的最好的就是描述你无数问题的修复方法。


你的问题修改了一点。

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

生活例子。

现在,您仍然需要传递CSubObject<EObjectTag, EObjectTag::CAT_A>作为第二个参数。

你也可以添加专门化:

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

如果你有template<EObjectTag tag> struct Example;,你也可以用CObject< EObjectTag, Example<EObjectTag::bob> >

我做了一些修改使它可以编译。虽然我不能100%确定这是否真的做了你想要的它做的;我同意雅克在他的回答中所说的大部分。

注意:下面的将不能编译,因为我故意尝试将一种枚举类型与另一种枚举的值混合,以验证它确实会触发编译时错误,我认为这正是您所要求的。

#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;
}