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

时间:2011-08-02 18:53:23

标签: c++ metaprogramming generic-programming partial-specialization

假设我想编写一个泛型函数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);
}

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

4 个答案:

答案 0 :(得分:15)

可读替代[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);

答案 1 :(得分:15)

我想标记发送,因为:

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

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

答案 2 :(得分:10)

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

Barend,Boost.Geometry的主要作者,explains这两种方法都优先于后者。这在Boost.Geometry中广泛使用。以下是优点:

  • 没有必要实例化标签,因为它的唯一目的是区分
  • 很容易根据标签
  • 定义新的类型和常量
  • 参数可以在界面中反转,即说distance(point, polygon);distance(polygon, point);都只能有一个实现

答案 3 :(得分: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)
{
}