一点概述。我正在编写一个提供强类型定义的类模板;通过强类型定义我与一个只声明别名的常规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。
答案 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;
}