在部分特定的类上标记调度和静态方法
假设我想写一个generics函数void f<T>()
,如果T
是一个PODtypes,那么做一件事,如果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>()); }
另一种方法是使用部分专用types的静态成员函数:
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); }
使用一种方法的优点和缺点是什么? 你会推荐哪个?
我想标签调度,因为:
- 易于使用新标签进行扩展
- 易于使用的inheritance( 示例 )
- 在generics编程中这是相当普遍的技术
在第二个例子中添加第三个变体似乎很棘手。 当你想要添加,例如非POD的PODtypes,你将不得不取代bool
template <typename T, bool> struct f2;
用其他的东西( int
如果你喜欢=)),并用struct f2<T, another-type-value>
replace所有的struct f2<T, bool-value>
struct f2<T, another-type-value>
。 所以对于我来说,第二个变体看起来很难扩展。 如果我错了,请纠正我。
一个可读的替代[boost|std]::enable_if
,标签和部分专业化的简单的编译时间调度,我喜欢的是以下内容:
[请记住,布尔变换为整数,零长度数组是无效的,并且这些违规模板被丢弃(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);
其实两者都只是标签调度模式。 前者称为标签调度,后者称为标签调度 。
Barend是Boost.Geometry的主要作者, 解释了这两种方法,并倾向于后者。 这在广泛的Boost.Geometry中使用。 这里总结的优点:
- 标签实例化是没有必要的,因为它唯一的目的就是区分
- 根据标签定义新的types和常量是很容易的
- 参数可以在界面上反转,即
distance(point, polygon);
和distance(polygon, point);
都可以只有一个实现
我知道这是一个老问题,答案已经被接受,但这可能是一个可行的select:
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) { }