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

发布于 2024-11-18 16:35:33 字数 416 浏览 0 评论 0原文

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;
  }
};

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

The STL functors are implemented like this:

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

This makes us mention the (possibly long) type everytime we create such a functor. Why are they not implemented like shown below? Any reasons?

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

That would make them usable without any mentioning of (possibly long) types.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

清欢 2024-11-25 16:35:33

这也使得不可能将它们专门用于用户定义的类型。

它们应该是一个定制点。


总结一下评论中的讨论:

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

如果允许用户专门化模板的各个功能,那么编写工作类模板将非常困难。然而,在某些情况下,将整个类专门用于用户定义的类型可能是个好主意。

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

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

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

新的 C++11 标准对此部分进行了扩展,并对其进行了更详细的说明 (17.6.4.2):

如果 C++ 程序向命名空间 std 或命名空间 std 内的命名空间添加声明或定义,则其行为是未定义的,除非另有说明。仅当声明依赖于用户定义的类型并且专门化满足原始模板的标准库要求并且未显式地表示时,程序才可以将任何标准库模板的模板专门化添加到命名空间 std
禁止。

如果 C++ 程序声明了,则其行为是未定义的

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

仅当声明依赖于用户定义类型的名称并且实例化满足原始模板的标准库要求时,程序才可以显式实例化标准库中定义的模板。

It would also make it impossible to specialize them for user defined types.

They are supposed to be a customization point.


To summarize the discussions in the comments:

Although it is technically possible to do like Xeo suggests, the language standard doesn't allow it.

It is very hard to write a working class template if users are allowed to specialize individual functions of the template. In some cases it might however be a good idea to specialize the whole class for a user defined type.

Therefore the C++98 standard writes (17.4.3.1):

It is undefined for a C++ program to add declarations or definitions to namespace std or namespaces within namespace std unless otherwise specified. A program may add template specializations for any standard library template to namespace std.

As it isn't "otherwise specified" that Xeo's code is allowed, we are to understand that it is not. Perhaps not totally obvious! Or that "template specializations" only apply to classes.

The new C++11 standard has had this part expanded, and spells it out in more detail (17.6.4.2):

The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly
prohibited.

The behavior of a C++ program is undefined if it declares

— an explicit specialization of any member function of a standard library class template, or
— an explicit specialization of any member function template of a standard library class or class template, or
— an explicit or partial specialization of any member class template of a standard library class or class template.

A program may explicitly instantiate a template defined in the standard library only if the declaration depends on the name of a user-defined type and the instantiation meets the standard library requirements for the original template.

燕归巢 2024-11-25 16:35:33

也许:

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

做的事情很奇怪,但重点是 std::lessstd::lessstd::lessunsigned int> 实现不同的数学函数,当传递(转换的结果)某些参数表达式时,这些函数会产生不同的结果。各种算法和其他标准库组件通过指定函子来工作,因此对我来说,有不同的函子来表示这些不同的数学函数,而不仅仅是一个函子上的 operator() 的不同重载是有意义的。

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

另外,还有一个高度推测性的说明 - 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 ;-)

Maybe:

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

Odd thing to do, but the point is that std::less<int>, std::less<long>, std::less<unsigned int> implement different mathematical functions which produce different results when passed (the result of converting) certain argument expressions. Various algorithms and other standard library components work by specifying a functor, so it makes sense to me that there are different functors to represent those different mathematical functions, not just different overloads of operator() on one functor.

Furthermore, a functor with a template operator() can't be an Adaptable Binary Predicate, since it doesn't have argument types (an argument can have any type). So if std::less were defined as you suggest then it couldn't participate in the stuff in <functional>.

Also on a highly speculative note -- std::less was probably designed before support for template member functions was at all widespread, since there are various notes in the SGI STL documentation that say, "if your implementation doesn't support member templates then this isn't available". For such a simple component there would, I guess, be an incentive to do something that works today. Once it exists, the standardization could then have removed it in favour of something else, but was it worth disrupting existing code? If it was that big a deal, then either you or the standard could introduce a flexible_less functor as you describe.

Finally, why

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

rather than

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

For user-defined types, the two might not be the same. Yes, this is an unfair question, since I don't know why there's no two-template-argument version of std::less ;-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文