部分专用类上的标记调度与静态方法
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 <typename T, bool> struct f2;
中的 bool
替换为其他内容(int
,如果您喜欢 =))并替换所有 struct f2<T, bool-value>
与struct f2<T, another-type-value>
。所以对我来说,第二个变体看起来很难扩展。如果我错了,请纠正我。
【讨论】:
扩展它的一种方法是使用枚举模板参数,但我同意它会有点笨重。【参考方案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<whatever>::type
真的混淆了whatever
部分恕我直言。在这里,它在括号内很清楚。
引用侏罗纪公园的话,“聪明的女孩”。为了使其更具可读性,您不应该选择使用 '= NULL' 还是更好的 '= nullptr'?
@AlexandreC。它们可能已经同样“WTF”,但enable_if
显然对搜索引擎更友好。
@FrankHB:我同意这一点——最终这取决于您团队的代码约定。然而,最好的办法可能是远离 SFINAE,除非真的需要,正是因为 WTF 因素。以上是关于部分专用类上的标记调度与静态方法的主要内容,如果未能解决你的问题,请参考以下文章