X 的任何子类的专用类模板,无需向模板添加其他类型参数

Specializing class template for any subclass of X without adding an additional type argument to the template

本文关键字:添加 类型参数 其他 子类 专用 任何      更新时间:2023-10-16

我需要专业化

template< typename T, int Id >
struct ValueTraits
{
  // Default version
  static int getValue() { return 0; }
};

对于某些ConcreteClass的任何子类,T

class ConcreteClass {};
struct ConcreteSub1: public ConcreteClass
{
  static int get() { return 1; }
};
struct ConcreteSub2: public ConcreteClass
{
  static int get() { return 2; }
};

对于Id的某个预定值,比如123

。这样ValueTraits< ConcreteSub1, 123 >::getValue()会调用ConcreteSub1::get()并返回1,而ValueTraits< ConcreteSub2, 123 >::getValue()会调用ConcreteSub2::get()并返回2。使用除 123 以外的任何Id或除 ConcreteClass 子类以外的类应回退到模板的默认版本。

现在,我知道我可以将std::enable_ifstd::is_base_of一起使用,但这不需要向ValueTraits添加一个额外的虚拟类型参数吗?像这样的东西会起作用,例如:

#include <stdio.h>
#include <type_traits>
template< typename T, int Id, typename Dummy = void >
struct ValueTraits
{
  // Default version
  static int getValue() { return 0; }
};
class ConcreteClass {};
struct ConcreteSub1: public ConcreteClass
{
  static int get() { return 1; }
};
struct ConcreteSub2: public ConcreteClass
{
  static int get() { return 2; }
};
template< typename T >
struct ValueTraits< T, 123, typename std::enable_if<
  std::is_base_of< ConcreteClass, T >::value >::type >
{
  static int getValue() { return T::get(); }
};
int main()
{
  // prints 1, 2, 0
  printf( "%d, %d, %dn",
          ValueTraits< ConcreteSub1, 123 >::getValue(),
          ValueTraits< ConcreteSub2, 123 >::getValue(),
          ValueTraits< int, 123 >::getValue() );
  return 0;
}

问题是,我无法向ValueTraits添加额外的虚拟类型参数,因为它是库的一部分,该库实际上为我提供了这种专攻ValueTraits

所以我的问题是,我可以用原始版本的ValueTraitstypenameint来执行这种专业化吗?

编辑:澄清一下,我也不是ValueTraits的用户 - 库为我提供了专门化,然后库再次使用我的类型实例化它,以获得我在专业化中定义的行为。所以我既不能控制ValueTraits的定义,也不能控制之后的使用方式。

GCC 4.8+ 可以,但不能 Clang:

template< typename T >
struct ValueTraits< T, std::enable_if<std::is_base_of< ConcreteClass, T >::value,
                                      std::integral_constant<int, 123> >::type::value >
{
  static int getValue() { return T::get(); }
};

我倾向于认为 Clang 目前是对的,但 CWG 1315 可能会更改此处的规则,以便上述代码变得有效,因此如果您只需要它来在 GCC 4.8 或更高版本上工作,您可能是安全的。

有趣的问题,但如果您提供一些额外的用例或更多信息,它会更有帮助,因为它的使用方式可能会改变实现的细节。

好的,现在回到答案,所以考虑到那里有一个你不能在这里修改的库,你可以这样做:

首先有一个你想要的任何形状或形式的类:

template< typename T, typename Dummy>
struct MyOwnSpecializedClassInAnyWayIWant
{
    // your own specialization:
    static int getValue() { return 0; }
};

现在,您只需要专攻一次ValueTraits

template< typename T , typename Dummy = void >
struct ValueTraits < MyOwnSpecializedClassInAnyWayIWant<T, Dummy> >
{
    // Default version
    static int getValue() { return MyOwnSpecializedClassInAnyWayIWant<T, Dummy>::getValue(); }
};

用例如下所示:

ValueTraits< MyOwnSpecializedClassInAnyWayIWant< ConcreteSub1 >>::getValue()

现在这样写有点痛苦,所以你可以使用:

template <class T>
using ValueTraits_t = ValueTraits< MyOwnSpecializedClassInAnyWayIWant< T > >;
//use ValueTraits_t<ConcreteSub1>;

作为旁注,为了简单起见,我删除了Id,但这也可以使用它。