51

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

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

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);
}

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

4

4 回答 4

17

我想要标签调度,因为:

  • 易于使用新标签进行扩展
  • 易于使用的继承(示例
  • 这是泛型编程中相当常见的技术

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

于 2011-08-03T13:00:21.127 回答
16

我喜欢的简单编译时调度的, tags 和部分专业化的可读替代方案如下:[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);
于 2011-08-02T19:17:29.297 回答
12

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

Boost.Geometry 的主要作者 Barend解释了这两种方法并更喜欢后者。这在 Boost.Geometry 中被广泛使用。以下是总结的优点:

  • 没有必要实例化标签,因为它的唯一目的是区分
  • 基于标签定义新类型和常量很容易
  • 接口中的参数可以颠倒,也就是说distance(point, polygon);distance(polygon, point);两者都只能有一个实现
于 2013-10-07T10:15:46.300 回答
2

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

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)
{
}
于 2015-10-21T19:35:49.327 回答