为什么将std :: vector和std :: string的比较运算符定义为模板函数?

Nir*_*man 14 c++ templates vector c++11

一点概述.我正在编写一个提供强大typedef的类模板; 通过强类型定义我与一个只声明别名的常规typedef形成对比.提出一个想法:

using EmployeeId = StrongTypedef<int>;
Run Code Online (Sandbox Code Playgroud)

现在,强类型定义和隐式转换有不同的思想流派.其中一所学校说:并非每个整数都是EmployeeId,但每个EmployeeId都是一个整数,所以你应该允许从EmployeeId到整数的隐式转换.你可以实现这个,并编写如下内容:

EmployeeId x(4);
assert(x == 4);
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为x隐式转换为整数,然后使用整数相等比较.到现在为止还挺好.现在,我想用一个整数向量来做这个:

using EmployeeScores = StrongTypedef<std::vector<int>>;
Run Code Online (Sandbox Code Playgroud)

所以我可以这样做:

std::vector<int> v1{1,2};
EmployeeScores e(v1);
std::vector<int> v2(e); // implicit conversion
assert(v1 == v2);
Run Code Online (Sandbox Code Playgroud)

但我还是不能这样做:

assert(v1 == e);
Run Code Online (Sandbox Code Playgroud)

这不起作用的原因是因为如何std::vector定义其等式检查,基本上(模数标准):

template <class T, class A>
bool operator==(const vector<T,A> & v1, const vector<T,A> & v2) {
...
}
Run Code Online (Sandbox Code Playgroud)

这是一个功能模板; 因为它在早期的查找阶段被丢弃,所以不允许将隐式转换为vector的类型进行比较.

定义平等的另一种方式是这样的:

template <class T, class A = std::allocator<T>>
class vector {
... // body

  friend bool operator==(const vector & v1, const vector & v2) {
  ...
}

} // end of class vector
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,相等运算符不是函数模板,它只是与类一起生成的常规函数​​,类似于成员函数.这是由friend关键字启用的一个不寻常的案例.

问题(抱歉背景太长了),为什么不std::vector使用第二种形式而不是第一种形式呢?这使得vector行为更像原始类型,并且您可以清楚地看到它有助于我的用例.这种行为更令人惊讶,string因为很容易忘记它string只是类模板的typedef.

我考虑过两件事:首先,有些人可能认为因为友元函数是通过类生成的,如果向量的包含类型不支持相等比较,这将导致硬故障.不是这种情况; 与模板类的成员函数一样,如果未使用,则不会生成它们.其次,在更一般的情况下,自由函数的优点是它们不需要在与类相同的头中定义,这可以具有优势.但这显然没有在这里使用.

那么,是什么给出的?这有充分的理由,还是仅仅是次优选择?

编辑:我写了一个快速示例,演示了两件事:隐式转换按照朋友的方法工作,并且如果模板化类型不满足相等运算符的要求,则不会导致硬故障(显然,假设在这种情况下不使用相等运算符).编辑:改进与第一种方法形成鲜明对比:http://coliru.stacked-crooked.com/a/6f8910945f4ed346.

Lea*_*elo 0

编辑:在我重新阅读我的解释并受到周围一些评论的影响后,我确信我最初的推理确实并不令人信服。我的回答本质上是试图论证,虽然一个值x可以隐式转换为y不同类型的值,但不一定期望两者之间进行“自动”相等比较。为了上下文化,我仍然保留我用作示例的代码。

struct B {};

template <class T>
struct A {
  A() {}
  A(B) {}
  friend bool operator==(const A<T>&, const A<T>&) { return false; }
};

// The template version wouldn't allow this to happen.
// template <class T>
// bool operator==(const A<T>&, const A<T>&) { return false; }

int main() {
  A<B> x;
  B y;
  if (x == y) {} //compiles fine
  return 0;
}
Run Code Online (Sandbox Code Playgroud)