部分专用类上的标记调度与静态方法

Posted

技术标签:

【中文标题】部分专用类上的标记调度与静态方法【英文标题】:Tag dispatch versus static methods on partially specialised classes 【发布时间】:2011-10-18 12:38:31 【问题描述】:

假设我想编写一个通用函数void f<T>(),如果T 是POD 类型,它会做一件事,如果T 是非POD(或任何其他任意谓词),它会做另一件事。

实现这一点的一种方法是使用标签调度模式,就像标准库对迭代器类别所做的那样:

template <bool> struct podness ;
typedef podness<true> pod_tag;
typedef podness<false> non_pod_tag;

template <typename T> void f2(T, pod_tag)  /* POD */ 
template <typename T> void f2(T, non_pod_tag)  /* non-POD */ 

template <typename T>
void f(T x)

    // Dispatch to f2 based on tag.
    f2(x, podness<std::is_pod<T>::value>());

另一种方法是使用部分专用类型的静态成员函数:

template <typename T, bool> struct f2;

template <typename T>
struct f2<T, true>  static void f(T)  /* POD */  ;

template <typename T>
struct f2<T, false>  static void f(T)  /* non-POD */  ;

template <typename T>
void f(T x)

    // Select the correct partially specialised type.
    f2<T, std::is_pod<T>::value>::f(x);

使用一种方法相对于另一种方法的优缺点是什么?你会推荐哪个?

【问题讨论】:

无论你的船漂浮什么。我发现第二个版本更“典型”和吸引人,因为辅助代码更少,隐藏的概念也更少。另外我会为参数添加转发! 【参考方案1】:

我知道这是一个已经接受答案的老问题,但这可能是一个可行的选择:

template<typename T>
std::enable_if_t<std::is_pod<T>::value> f(T pod)



template<typename T>
std::enable_if_t<!std::is_pod<T>::value> f(T non_pod)


【讨论】:

【参考方案2】:

实际上两者都只是标签调度模式。前者称为标签分派,后者标签分派

Barend,Boost.Geometry 的主要作者,explains 两种方法都喜欢后者。这在 Boost.Geometry 中被广泛使用。以下是总结的优势:

没有必要实例化标签,因为它的唯一目的是区分 很容易根据标签定义新的类型和常量 接口中的参数可以颠倒,例如 distance(point, polygon);distance(polygon, point); 只能有一个实现

【讨论】:

【参考方案3】:

我想要标签调度,因为:

使用新标签轻松扩展 易于使用的继承 (example) 这是泛型编程中相当常见的技术

在第二个示例中添加第三个变体对我来说似乎很棘手。当您要添加时,例如非 POD-of-POD 类型,您必须将 template &lt;typename T, bool&gt; struct f2; 中的 bool 替换为其他内容(int,如果您喜欢 =))并替换所有 struct f2&lt;T, bool-value&gt;struct f2&lt;T, another-type-value&gt;。所以对我来说,第二个变体看起来很难扩展。如果我错了,请纠正我。

【讨论】:

扩展它的一种方法是使用枚举模板参数,但我同意它会有点笨重。【参考方案4】:

[boost|std]::enable_if可读 替代品,我喜欢的 simple 编译时调度的标签和部分特化如下:

[请记住,布尔值可以转换为整数,零长度数组是无效的,并且有问题的模板会被丢弃 (SFINAE)。此外,char (*)[n] 是指向 n 元素数组的指针。]

template <typename T> 
void foo(T, char (*)[is_pod<T>::value] = 0)

    // POD


template <typename T> 
void foo(T, char (*)[!is_pod<T>::value] = 0)

    // Non POD

它还具有不需要污染命名空间的外部类的优点。现在,如果您想将问题中的谓词外部化,您可以这样做:

template <bool what, typename T>
void foo(T, char (*)[what] = 0)

    // taken when what is true


template <bool what, typename T>
void foo(T, char (*)[!what] = 0)

    // taken when what is false

用法:

foo<std::is_pod<T>::value>(some_variable);

【讨论】:

是的,它很聪明,但如果您不了解 SFINAE,那么它有一个其他人没有的严重“WTF”因素。不过,我很欣赏简洁。 @Peter:SFINAE 已经有了很大的 WTF 因素。使用typename std::enable_if&lt;whatever&gt;::type 真的混淆了whatever 部分恕我直言。在这里,它在括号内很清楚。 引用侏罗纪公园的话,“聪明的女孩”。为了使其更具可读性,您不应该选择使用 '= NULL' 还是更好的 '= nullptr'? @AlexandreC。它们可能已经同样“WTF”,但enable_if 显然对搜索引擎更友好。 @FrankHB:我同意这一点——最终这取决于您团队的代码约定。然而,最好的办法可能是远离 SFINAE,除非真的需要,正是因为 WTF 因素。

以上是关于部分专用类上的标记调度与静态方法的主要内容,如果未能解决你的问题,请参考以下文章

C#与Java在继承静态类上的区别

Java:静态类?

如何在变量类上调用静态方法?

如何在派生类上动态调用静态方法

Static(静态)关键字入门

静态代码块-普通代码块-构造代码块(好多图系列)