【问题标题】:Specializing class template for any subclass of X without adding an additional type argument to the template为 X 的任何子类专门化类模板,而不向模板添加额外的类型参数
【发布时间】:2016-02-17 17:47: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&lt; ConcreteSub1, 123 &gt;::getValue() 将调用ConcreteSub1::get() 并返回1,而ValueTraits&lt; ConcreteSub2, 123 &gt;::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, %d\n",
          ValueTraits< ConcreteSub1, 123 >::getValue(),
          ValueTraits< ConcreteSub2, 123 >::getValue(),
          ValueTraits< int, 123 >::getValue() );
  return 0;
}

问题是,我不能向ValueTraits 添加额外的虚拟类型参数,因为它是一个库的一部分,它实际上提供了这个ValueTraits 供我专门研究。

所以我的问题是,我可以使用 ValueTraits 的原始版本仅使用 typenameint 来执行此专业化吗?如何?

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

【问题讨论】:

  • 将原始 ValueTraits 子类化为采用 3 个模板参数的扩展版本?
  • 我认为这不会为 ValueTraits 本身添加任何专业化 - 请参阅编辑。
  • 您需要使用哪些编译器?
  • 我想说代码应该是 c++11 兼容的。我宁愿不依赖任何特定编译器的任何(错误)行为。

标签: c++ templates template-specialization sfinae enable-if


【解决方案1】:

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 或稍后。

【讨论】:

  • 这非常棒,使用integral_constant 作为enable_if 的类型,然后访问它的值...是的,我感觉指定这样的非类型参数违反了标准,因为它涉及一个模板参数 T。它可能是一个 gcc 错误,这会使依赖当前行为有风险。不过,我真的很喜欢这个integral_constant 把戏。
  • @dragonroot 就像我说的,CWG 1315 应该(希望很快)使这个正式的 (TM) 有效代码。目前我想不出更好的办法。
  • @dragonroot 毫无疑问是 C++17 的一部分(C++11 已得到长期认可)。我预计它会在 Rapperswil(3 月初)得到解决。
  • @Columbo 你是说杰克逊维尔?无论如何,编译器通常都会追溯地实现 DR。 (另外,好论文。)
  • @T.C.对不起;杰克逊维尔。 :) 关于 DR:当然,它们不会追溯解决吗?或者这无关紧要?
【解决方案2】:

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

好的,现在回到答案,所以考虑到那里有一个您无法在此处修改的库,您可以这样做:

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

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,但这也可以使用。

【讨论】:

  • 我已编辑问题以澄清这一点。 ValueTraits 也由库实例化,因此我无法控制它如何被实例化。我只应该为它提供专业化,到目前为止我只能弄清楚如何做更简单的。
  • @dragonroot 我认为即使库是ValueTraits 的用户,这样的方法仍然可以工作。对于这个问题我没有其他解决方案,所以我将把我的回复留在这里记录下来。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多