C++ 选择模板函数的重载还是函子(函数对象)的部分特殊化

C++ 选择模板函数的重载还是函子(函数对象)的部分特殊化,c++,stl,template-specialization,generic-programming,sgi,C++,Stl,Template Specialization,Generic Programming,Sgi,以下是g++的STL实现(STL的sgi版本)的摘录。我想知道为什么他们使用部分专门化而不是函数重载 template <class InputIterator, class OutputIterator> struct __copy_dispatch { OutputIterator operator()(InputIterator first, InputIterator last, OutputIterator res

以下是g++的STL实现(STL的sgi版本)的摘录。我想知道为什么他们使用部分专门化而不是函数重载

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);
}
看起来它也很好用

那么是否还有其他理由使用带有部分专门化的函子类而不是函数重载

更新

以下是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());
//sgi 4.5
模板
结构破坏辅助
{
模板
静态空隙
__销毁(_ForwardIterator uu first,_ForwardIterator uu last)
{
对于(;\uuuu first!=\uuuu last;++\uuu first)
std::_销毁(&*u优先);
}
};
 
模板
结构破坏辅助
{
模板
静态空隙
__销毁(_ForwardIterator,_ForwardIterator){}
 
};
//在旧版本的SGI2.9中,这是通过函数重载实现的
模板
内联空隙
__销毁\u aux(ForwardIterator first,ForwardIterator last,\uuuu false\u type){
for(;first
,类模板无法推断其参数。这导致函数模板重载和普通函数作为部分和显式专门化的代理的不规则模式

为了将这两个方面的优点结合起来,大多数通用代码都会按照您在问题中所展示的那样执行

//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);
}
//复制的通用版本
模板
内联输出迭代器副本(先输入迭代器,后输入迭代器,
输出计数器结果)
{
返回uu copy_dispatch()(第一个、最后一个、结果);
}
顶级函数模板的参数是推导出来的(这有助于用户编写紧凑的代码),内部类模板专门用于各种特殊情况(这有助于编译器实现优化)。内部级别函数对象周围的顶级函数包装器由每个体面的编译器内联,因此没有开销

更新:正如您所注意到的,从技术上讲,您可以通过使用函数模板重载替换部分类模板专门化,并使用普通函数替换显式类模板专门化来实现相同的效果(而不是允许的显式函数模板专门化,正如Sutter专栏中所解释的,它不会参与重载解析)


因为函数模板委托给类模板函数对象对于部分和显式专门化的行为更为规则,所以库编写者和用户在需要时维护或修改它就不那么微妙了。这是保持简单的原则。

这主要是一个偏好问题。任何编译时调度ng对于可以使用类模板部分专用化完成的函数调用,您也可以使用函数模板重载,反之亦然。事实上,标准通过引用重载函数模板的偏序定义了类模板部分专用化的偏序(14.5.5.2)


这就是说,有时您需要一个编译时选项来控制要调用的单个函数以外的内容。类模板可以有任意数量的成员函数和成员typedef,而不仅仅是一个函数。在C++11的
constexpr
之前,静态类成员是设置cons的最佳方式tant表达式依赖于模板参数,因此其专门化可以用作模板参数、数组边界等。

我将添加一个案例,说明您的解决方案存在“问题”


模板无效foo(T){std::cout问题是关于重载函数模板的,而不是函数模板的专门化。我的问题实际上是关于内部类模板的,我可以像第二段代码那样使用函数重载吗?如果可以,为什么不在STL实现中使用它。您的代码在这种特殊情况下工作。但是作为链接的ar关于函数模板专门化的文章显示,一般来说,它充满了非常微妙的惊喜。像g++标准库的实现者这样有经验的程序员试图通过委托给一个他们可以部分专门化的类模板来避免这种情况。@TemplateRex是的,我正在阅读,这是一个很好的参考。向上!@aschepler重点是d使用g++的方式是惯用的,虽然建议的重载在这种情况下可以工作,但是如果另一个开发人员决定添加一个“只是为了处理int*的特殊情况”,您可以很容易地进入函数专门化领域例如,经过深思熟虑后,我更新了我的答案。从技术上讲,你可以用两种方法来完成,正如你自己发现的那样。我认为没有任何技术上的区别(即,我无法给出一种方法会选择与另一种方法不同的功能的示例)但是,将函数模板委托给类模板函数对象更规则,更易于维护。这是我能想到的最好的理由:-)
//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());
//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);
}
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)
}
copy_dispatch<char*, char*>