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

Pet*_*der 47 c++ metaprogramming partial-specialization generic-programming

假设我想编写一个泛型函数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>());
}
Run Code Online (Sandbox Code Playgroud)

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

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);
}
Run Code Online (Sandbox Code Playgroud)

使用一种方法比另一种方法有什么优缺点?你会推荐哪个?

Ale*_* C. 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
}
Run Code Online (Sandbox Code Playgroud)

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

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
}
Run Code Online (Sandbox Code Playgroud)

用法:

foo<std::is_pod<T>::value>(some_variable);
Run Code Online (Sandbox Code Playgroud)

  • @AlexandreC.它们可能已经同样是"WTF"但是"enable_if"显然更适合搜索引擎. (3认同)
  • 是的,它很聪明,但是如果您不了解 SFINAE,那么这具有其他人没有的严重的“WTF”因素。不过我很欣赏简洁。 (2认同)
  • @Peter:SFINAE已经拥有很大的WTF要素。使用`typename std :: enable_if &lt;whatever&gt; :: type`确实模糊了`whatever`部分的恕我直言。在这里,它在方括号内清晰可见。 (2认同)

tim*_*tim 15

我想标记调度,因为:

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

在第二个例子中添加第三个变体对我来说似乎很棘手.当你需要添加,例如非POD的荚键入你要替换booltemplate <typename T, bool> struct f2;与其他东西(int如果你喜欢=)),并更换所有struct f2<T, bool-value>struct f2<T, another-type-value>.所以对我来说,第二个变体看起来难以扩展.如果我错了,请纠正我.


leg*_*s2k 10

实际上两者都只是标签调度模式.前者通过实例调用标签调度,后者按类型调用标签.

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

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