为什么标准容器使用函数模板而不是非模板Koenig运算符

Lin*_*gxi 14 c++ standard-library implicit-conversion c++11 reference-wrapper

这个问题的灵感来自于std :: reference_wrapper的问题.让我们说,例如,operator<对于std::vector.它被定义为函数模板

template< class T, class Alloc >
bool operator<( const vector<T,Alloc>& lhs,
                const vector<T,Alloc>& rhs );
Run Code Online (Sandbox Code Playgroud)

因此,拒绝将函数参数隐式转换为相应函数参数的类型(主要是因为其模板性质).这大大降低了它的实用性和便利性std::reference_wrapper.例如,您不能使用std::sortstd::vector<std::reference_wrapper<std::vector<int>>>.

另一方面,只有当operator<定义为非模板Koenig运算符时,才能解决所有问题

template <...>
class vector ... {
  friend bool operator<(const vector& a, const vector& b) {...}
};
Run Code Online (Sandbox Code Playgroud)

我想知道为什么标准库采用了前一种方法而不是这种方法?

Nir*_*man 1

考虑这段代码(啊):

\n\n
template <class T>\nclass A {\n  public:\n  T m_x;\n\n  friend bool operator<(const A & lhs, const A & rhs) {\n    return lhs.m_x < rhs.m_x;\n  }\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

和main.cpp:

\n\n
#include "A.h"\n\nnamespace buddy {\nbool operator<(const A<double> & lhs, const A<double> &rhs) {\n    return lhs.m_x > rhs.m_x;\n};\n}\nusing namespace buddy;\nint main(int argc, char ** argv) {\n\n  A<double> a1;\n  A<double> a2;\n\n  a1 < a2;\n\n  return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

此代码无法编译:

\n\n

main.cpp:14:5: 错误: \xe2\x80\x98operator<\xe2\x80\x99 的重载不明确(操作数类型为 \xe2\x80\x98A\xe2\x80\x99 和 \xe2\x80\x98A\ xe2\x80\x99)\n a1 < a2;

\n\n

当然,原因是两个运算符 <\ 是完全匹配的。另一方面,如果我们将第一个运算符 < 更改为(在类外部定义):

\n\n
template <class T>\nbool operator<(const A<T> & lhs, const A<T> & rhs) {\n  return lhs.m_x < rhs.m_x;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

编译器不再抱怨:现在是精确匹配和函数模板之间的竞争,因此使用精确匹配。

\n\n

如果operator<是以您建议的方式定义的,则std::vector的用户将没有合理的方法来重新定义operator<的行为,除非专门化std::vector本身,这要多得多工作。

\n\n

总之,标准编写者选择让重载操作符<变得更容易,而不是提供在某些情况下可能更有用的操作符<。我认为他们做出了正确的选择。

\n