请看下面的代码:
template<typename T>
struct A
{
T i;
// friend bool operator<(A const& l, A const& r) { return l.i < r.i; }
};
template<typename T> bool operator<(A<T> const& l, A<T> const& r) { return l.i < r.i; }
struct B
{
float f;
template<typename T> operator A<T>() const { return A<T>{static_cast<T>(f)}; }
};
int main()
{
bool a = A<int>{3} < A<int>{4};
bool b = A<int>{3} < B{5.4f};
}
通过 friend operator 实现的比较可以支持 A 与 B 的比较,而在外面直接实现 operator 则会导致 A 与 B 的比较编译出错,参考:
https://godbolt.org/z/xsBlnA
这是为啥呢?如果不借助 friend 如何才能使得 A 与 B 可以正常比较?
1
429839446 2018-11-26 22:47:34 +08:00
隐式转换和模板实例化以及函数重载决议的冲突,有点忘了。
|
2
429839446 2018-11-26 22:47:56 +08:00
请楼主自行看书或者 cpp ref
|
3
wutiantong OP @429839446 你之前遇到过这个问题么?
|
4
feverzsj 2018-11-27 00:14:45 +08:00 2
调用模板函数,首先要进行模板推演,编译器还不会聪明到同时做隐式类型转换,而 friend 那个不是模板函数,因为在你实例化 A<int>时,这个函数就已经确定了,编译器可以直接进行隐式类型转换
|
5
wutiantong OP @feverzsj 有道理
|
6
wutiantong OP @feverzsj 这是否意味着将此类 operator 或 function 声明为 friend 放在 template class 内部(总)是一种更好的实践?
|
7
GeruzoniAnsasu 2018-11-27 10:37:46 +08:00 via Android
@wutiantong 避免隐式转换才是最佳实践
|
8
wutiantong OP @GeruzoniAnsasu 不认同这个说法,你在哪看到的?
|
9
GeruzoniAnsasu 2018-11-27 12:36:30 +08:00
@wutiantong 还真说不上来为什么,但感觉也说不上来隐式转换有什么必要性,从 A 隐式转换为 B 这个场景其实一般真正的目的只是想提供统一的接口,这完全可以通过调整抽象层次解决,A 转 B 的写法也必定是侵入式的,一定程度上破坏了封装,除非 B 是 A 的派生类型。但派生类自带多态,类型转换真的会用到吗
就比如这个比较 https://gist.github.com/pnck/8875c927da7aaa90e6da72a1a1245616 写成这样是不是会好一点? 注意使用接口类的 value()是多态特性,并不是隐式转换 |
10
wutiantong OP @GeruzoniAnsasu
1. STL 里面就有很常见的 std::basic_ios,std::shared_ptr 到 bool 的转换。 2. “ A 转 B 的写法也必定是侵入式的,一定程度上破坏了封装,除非 B 是 A 的派生类型” ,从侵入的角度来说,在 B 里面定义 operator A() 跟 把 B 定义为 A 的子类 是没有区别的,你再仔细想想? 3. 通过继承和多态引入的语义以及性能负担与定义隐式转换不可同日而语,它们有不同的使用场景。 4. 代码中的 UNIFORM_VALUE_TYPE 可以用 std::common_type 来改善。 5. 代码中 comparable 这个接口类实现了一个统一的比较接口,但它实际上并没有做任何具体的比较(而是委托给了两个 value 的 common_type 的比较函数),这跟我所要做的事情本质上并无关联。 |
11
wutiantong OP @GeruzoniAnsasu 上文第 2 点,我可能对“除非 B 是 A 的派生类型”这里有些误解,但我知道你想强调的是“隐式转换导致的耦合”。如果是把两个关联很弱的类硬拉凑到一起做个隐式转换,这的确会产生额外的耦合。但是“额外的耦合”这种问题本质上并不应归咎于一个无辜的语法特性,而应归咎于设计者的缺乏经验及不慎。
|