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

时间:2015-12-30 23:36:52

标签: c++ templates c++11 vector

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

using EmployeeId = StrongTypedef<int>;

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

EmployeeId x(4);
assert(x == 4);

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

using EmployeeScores = StrongTypedef<std::vector<int>>;

所以我可以这样做:

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

但我仍然无法做到这一点:

assert(v1 == e);

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

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

这是一个功能模板;因为它在早期的查找阶段被丢弃,所以不允许将隐式转换为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

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

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

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

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

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

2 个答案:

答案 0 :(得分:2)

您所描述的技术(我称之为Koenig运营商)在vector设计并最初指定时尚不清楚,至少不是广泛的。

现在改变它需要比最初使用它更多的关注,并且更多的理由。

作为猜测,今天将使用Koenig运算符代替模板运算符。

答案 1 :(得分:0)

编辑:在重新阅读我的解释并受到一些评论的影响之后,我确信我的原始推理确实并不引人注目。我的答案基本上试图争辩说,虽然值x可以隐式转换为不同类型的值y,但是&#34;自动地&#34;两者之间的平等比较可能不一定是预期的。对于情境化,我仍然留下我用作示例的代码。

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