为什么STL仿函数本身是模板化的,而不是它们的函数调用操作符?

时间:2011-07-04 20:52:15

标签: c++ templates stl functor

STL仿函数的实现如下:

template<class T>
struct less{
  bool operator()(T const& lhs, T const& rhs){
    return lhs < rhs;
  }
};

这使我们每次创建这样的仿函数时都会提到(可能很长)类型。为什么它们没有如下所示实现?有什么理由吗?

struct less{
  template<class T>
  bool operator()(T const& lhs, T const& rhs){
    return lhs < rhs;
  }
};

这将使它们可用而不提及(可能很长)类型。

2 个答案:

答案 0 :(得分:23)

这也使得无法将它们专门用于用户定义的类型。

它们应该是一个定制点。


总结评论中的讨论:

虽然技术上可以像Xeo建议的那样做,但语言标准不允许这样做。

如果允许用户专门化模板的各个功能,则很难编写工作类模板。在某些情况下,将整个类专门化为用户定义的类型可能是个好主意。

因此C ++ 98标准写入(17.4.3.1):

  

除非另有说明,否则C ++程序未定义向命名空间std中的命名空间std或命名空间添加声明或定义。程序可以将任何标准库模板的模板特化添加到命名空间std。

由于没有“另行指定”Xeo的代码是允许的,我们应该理解它不是。也许并不完全明显!或者“模板特化”仅适用于类。

新的C ++ 11标准扩展了这一部分,并详细说明了它(17.6.4.2):

  

如果C ++程序向命名空间std或命名空间std中的命名空间添加声明或定义,则该行为是未定义的,除非另有说明。只有当声明取决于用户定义的类型并且特化符合原始模板的标准库要求且未明确指定时,程序才可以将任何标准库模板的模板特化添加到命名空间std   禁止的。

     

如果声明

,则C ++程序的行为是未定义的      

- 标准库类模板的任何成员函数的明确特化,或者     - 标准库类或类模板的任何成员函数模板的明确特化,或者    - 标准库类或类模板的任何成员类模板的显式或部分特化。

     

只有当声明取决于用户定义类型的名称并且实例化符合原始模板的标准库要求时,程序才可以显式实例化标准库中定义的模板。

答案 1 :(得分:5)

也许:

std::multiset<long, std::less<int> > moduloset;

奇怪的是要做的事情,但重点是std::less<int>std::less<long>std::less<unsigned int>实现了不同的数学函数,这些函数在传递(转换结果)某些参数表达式时会产生不同的结果。各种算法和其他标准库组件通过指定仿函数来工作,因此我觉得有不同的仿函数来表示这些不同的数学函数,而不仅仅是operator()在一个仿函数上的不同重载。

此外,具有模板operator()的仿函数不能是自适应二进制谓词,因为它没有参数类型(参数可以具有任何类型)。因此,如果按照您的建议定义了std::less,则无法参与<functional>中的内容。

同样在一个高度推测性的说明中 - std::less可能是在对模板成员函数的支持普遍存在之前设计的,因为SGI STL文档中有各种说明,“如果您的实现没有支持成员模板然后这不可用“。对于这样一个简单的组件,我想,这是一种激励去做今天有用的事情。一旦它存在,标准化可以然后删除它而不是其他东西,但它是否值得破坏现有代码?如果这是一笔大交易,那么您或标准可以在您描述时引入flexible_less仿函数。

最后,为什么

template<class T>
bool operator()(T const& lhs, T const& rhs){
  return lhs < rhs;
}

而不是

template<class T, class U>
bool operator()(T const& lhs, U const& rhs){
  return lhs < rhs;
}

对于用户定义的类型,两者可能不同。是的,这是一个不公平的问题,因为我不知道为什么没有std::less的双模板参数版本; - )