简体   繁体   English

选择模板函数的重载还是函子(函数对象)的部分专业化

[英]Whether to choose an overload of template function or a partial specialization of a functor(function object)

The following is an excerpt of STL implementation of g++ (the sgi version of STL). 以下是g ++(STL的sgi版本)的STL实现的摘录。 I want to know why they use partial specialization instead of function overload. 我想知道为什么他们使用部分专业化而不是函数重载。

template <class InputIterator, class OutputIterator>
struct __copy_dispatch
{
  OutputIterator operator()(InputIterator first, InputIterator last,
                            OutputIterator result) {
    return __copy(first, last, result, iterator_category(first));
  }
};

//If the inputiterator and the outputiterator is all type T
//This is a partial specialization of the generalized version
template <class T>
struct __copy_dispatch<T*, T*>//-----------------------(1)
{
  T* operator()(T* first, T* last, T* result) {
    typedef typename __type_traits<T>::has_trivial_assignment_operator t; 
    return __copy_t(first, last, result, t());
  }
};

//Strictly speaking this is a partial specialization of the last template function
template <class T>
struct __copy_dispatch<const T*, T*>//-----------------(2)
{
  T* operator()(const T* first, const T* last, T* result) {
    typedef typename __type_traits<T>::has_trivial_assignment_operator t; 
    return __copy_t(first, last, result, t());
  }
};


//The generalized version of copy
template <class InputIterator, class OutputIterator>
inline OutputIterator copy(InputIterator first, InputIterator last,
                           OutputIterator result)
{
  return __copy_dispatch<InputIterator,OutputIterator>()(first, last, result);
}

//A overload version
inline char* copy(const char* first, const char* last, char* result) {
  memmove(result, first, last - first);
  return result + (last - first);
}

What if I use an overload version like: 如果我使用像这样的重载版本怎么办:

#include <iostream>

using namespace std;


template <class InputIterator, class OutputIterator>
OutputIterator copy_dispatch(InputIterator first, InputIterator last,
                            OutputIterator result) {
    cout << "now in first" << endl;
    return result;
}
template <class T>
T* copy_dispatch(T* first, T* last, T* result) {
    cout << "now in second" << endl;
    return 0;
}
template <class T>
T* copy_dispatch(const T* first, const T* last, T* result) {
    cout << "now in third" << endl;
    return 0;
}

int main( void ) {
    int a[]={1,2,3,4,5,6};
    double b[] = {1.0,2.0,3.0,4.0,5.0,6.0};
    int c[]={0,0,0,0,0,0};
    int const d[]={0,0,0,0,0,0};

    copy_dispatch(a,a+6, b);
    copy_dispatch(a, a+6, c);
    copy_dispatch(d, d+6, c);

}

The output is: 输出为:

now in first
now in second
now in third

Seems that it also works fine? 似乎还可以吗?

So is there any further reason to use a functor class with partial specialization instead of function overload 那么,有没有其他理由使用带有部分专业化的仿函数类而不是函数重载

UPDATES 更新

Here are some other excerpts from sgi implementation of STL: 这是STL的sgi实现的其他摘录:

//sgi 4.5
template<bool>
struct _Destroy_aux
{
  template<typename _ForwardIterator>
    static void
    __destroy(_ForwardIterator __first, _ForwardIterator __last)
    {
      for (; __first != __last; ++__first)
        std::_Destroy(&*__first);
    }
};
 
template<>
struct _Destroy_aux<true>
{
  template<typename _ForwardIterator>
    static void
    __destroy(_ForwardIterator, _ForwardIterator) { }
 
};

//in an old version of sgi 2.9 this is implemented with function overload
template <class ForwardIterator>
inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
  for ( ; first < last; ++first)
    destroy(&*first);
}
 
template <class ForwardIterator> 
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}
 
template <class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*) {
  typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
  __destroy_aux(first, last, trivial_destructor());

Function template specializations do not participate in overload resolution , and class templates cannot deduce their arguments. 函数模板专长不参与重载解析 ,并且类模板不能推论其参数。 This leads to the irregular pattern of function template overloads and ordinary functions as proxies for partial and explicit specializations. 这导致函数模板重载和普通函数作为部分和显式专业化的代理的不规则模式。

To combine the best of both worlds, most generic code does what you showed in your question 结合两全其美,大多数通用代码都可以完成您在问题中显示的内容

//The generalized version of copy
template <class InputIterator, class OutputIterator>
inline OutputIterator copy(InputIterator first, InputIterator last,
                           OutputIterator result)
{
  return __copy_dispatch<InputIterator,OutputIterator>()(first, last, result);
}

The top-level function template has its arguments deduced (which helps users write compact code), and the inner class template is specialized for the various special cases (that helps the compiler enable optimizations). 顶层函数模板具有推导的参数(可帮助用户编写紧凑的代码),而内部类模板则专门用于各种特殊情况(可帮助编译器实现优化)。 The top-level function wrapper around the inner-level function object is inlined by every decent compiler, so there is no overhead. 每个不错的编译器都会内联内层函数对象的顶层函数包装器,因此没有开销。

UPDATE : As you notice yourself, technically , you can achieve the same effects by replacing partial class template specialization with function template overloading, and explicit class template specialization with an ordinary function (instead of the allowed explicit function template specialization, which, as explained in Sutter's column, would not participate in overload resolution). 更新 :从技术上您已经注意到,可以通过用函数模板重载替换部分类模板专业化和用普通函数替换显式类模板专业化来实现相同的效果(而不是允许的显式函数模板专业化,如Sutter的专栏,将不会参与重载解析。

Because the function template delegating to a class template function object behaves more regularly for both partial and explicit specializations, it is less subtle for library writers and users alike to maintain, or modify when required. 因为委派给类模板函数对象的函数模板在部分和显式专业化方面表现得更为规律,所以对于库编写者和用户(无论是需要维护还是在需要时进行修改)而言,它的精妙性就较小。 It's the keep it simple principle. 这是保持简单的原则。

This is mostly a matter of preference. 这主要是一个优先事项。 Any compile-time dispatching for a function call that can be done with class template partial specializations, you could also do with function template overloads, and vice versa. 可以使用类模板部分专业化功能进行的任何函数调用编译时分派,也可以使用函数模板重载来完成,反之亦然。 In fact, the Standard defines the partial ordering on class template partial specializations by referring to the partial ordering on overloaded function templates (14.5.5.2). 实际上,该标准通过引用重载函数模板的部分排序(14.5.5.2)来定义类模板部分专业化的部分排序。

That said, sometimes you want a compile-time choice to control something other than or more than just which single function to call. 就是说,有时您希望编译时选择控制除调用单个函数以外的其他功能。 Class templates can have any number of member functions and member typedefs, not just the one function. 类模板可以具有任意数量的成员函数和成员typedef,而不仅仅是一个函数。 And prior to C++11's constexpr , a static class member was by far the best way to set up a constant expression dependent on template parameters, so that its specializations can be used as template parameters, array bounds, and so on. 而且,在C ++ 11的constexpr ,静态类成员是建立依赖于模板参数的常量表达式的最佳方法,因此可以将其专门化用作模板参数,数组边界等。

I would add a case your solution has "problem" 我要添加一个案例,说明您的解决方案存在“问题”

template <class T> void foo(T t) { std::cout << "foo<T>" << std::endl; }   // (a)
template <class T> void foo(T* t) { std::cout << "foo<T*>" << std::endl; } // (b)

void bar() {
    int i = 0;

    foo(i);          // call (a) f<int>(int)
    foo(&i);         // call (b) f<int>(int*)
    foo<int*>(&i);   // call (a) f<int*>(int*) and not (b)
}

So in your case if I call 所以在你的情况下,如果我打电话

copy_dispatch<char*, char*>

I don't get the specialization as it is in the STL. 我没有STL中的专业化知识。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM