Hidden Friends
下面的代码中,Bar 能够隐式转换成 Foo。想要重载 Foo 的等于运算符至少有三种方案:
- 重载全局的
==
运算符 - 重载 Foo 中的 hidden friend,即
friend bool operator==(Foo const &a, Foo const &b)
- 重载
bool operator==(Foo const &a) const
第一种方案会污染全局的名字空间,使得 Bar 和 Bar 之间也能通过转换两个参数进行比较;第三种方案允许第二个参数的隐式类型转换,但要求第一个参数必须是 Foo 类型;第二种方案只要求任一个参数为 Foo 类型,使得 ADL 能够参与找到这个函数,另一个参数则可以通过隐式类型转换来得到。
成员函数的劣势是由于查找方式和 this 指针的存在,函数失去了对称性。全局函数的劣势则是没有用上 ADL 的优势,hidden friends 在这种情况比较好。
https://godbolt.org/z/6sxdno3Yh
#include <iostream>
#define HIDDEN_FEIDEND 1
struct Foo {
int value{};
#ifdef HIDDEN_FEIDEND
friend bool operator==(Foo const &a, Foo const &b) {
return a.value == b.value;
}
#endif
};
struct Bar {
int value{};
// Bar can be converted implicitly to Foo.
// `explicit` keyword makes the conversion explicit.
// explicit
operator Foo() const {
return Foo{value};
}
};
#ifndef HIDDEN_FEIDEND
// Normal operator overload that can be found in global scope
bool operator==(Foo const &a, Foo const &b) {
return a.value == b.value;
}
#endif
int main() {
Bar a{-4}, b{1};
Foo c{2};
// std::cout << std::boolalpha << (a == b) << "\\n"; // ok if no HIDDEN_FEIDEND
std::cout << std::boolalpha << (a == c) << "\\n"; // ok
std::cout << std::boolalpha << (c == a) << "\\n"; // ok
}