放在 template class 里面的 friend binary-operator 和 放在外面的相同实现的 operator 在传参数上有什么差异呢?

2018-11-26 18:52:20 +08:00
 wutiantong

请看下面的代码:

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 可以正常比较?

2075 次点击
所在节点    C
11 条回复
429839446
2018-11-26 22:47:34 +08:00
隐式转换和模板实例化以及函数重载决议的冲突,有点忘了。
429839446
2018-11-26 22:47:56 +08:00
请楼主自行看书或者 cpp ref
wutiantong
2018-11-26 22:59:28 +08:00
@429839446 你之前遇到过这个问题么?
feverzsj
2018-11-27 00:14:45 +08:00
调用模板函数,首先要进行模板推演,编译器还不会聪明到同时做隐式类型转换,而 friend 那个不是模板函数,因为在你实例化 A<int>时,这个函数就已经确定了,编译器可以直接进行隐式类型转换
wutiantong
2018-11-27 00:19:39 +08:00
@feverzsj 有道理
wutiantong
2018-11-27 00:38:49 +08:00
@feverzsj 这是否意味着将此类 operator 或 function 声明为 friend 放在 template class 内部(总)是一种更好的实践?
GeruzoniAnsasu
2018-11-27 10:37:46 +08:00
@wutiantong 避免隐式转换才是最佳实践
wutiantong
2018-11-27 10:58:51 +08:00
@GeruzoniAnsasu 不认同这个说法,你在哪看到的?
GeruzoniAnsasu
2018-11-27 12:36:30 +08:00
@wutiantong 还真说不上来为什么,但感觉也说不上来隐式转换有什么必要性,从 A 隐式转换为 B 这个场景其实一般真正的目的只是想提供统一的接口,这完全可以通过调整抽象层次解决,A 转 B 的写法也必定是侵入式的,一定程度上破坏了封装,除非 B 是 A 的派生类型。但派生类自带多态,类型转换真的会用到吗

就比如这个比较

https://gist.github.com/pnck/8875c927da7aaa90e6da72a1a1245616

写成这样是不是会好一点?

注意使用接口类的 value()是多态特性,并不是隐式转换
wutiantong
2018-11-27 13:09:20 +08:00
@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 的比较函数),这跟我所要做的事情本质上并无关联。
wutiantong
2018-11-27 13:22:46 +08:00
@GeruzoniAnsasu 上文第 2 点,我可能对“除非 B 是 A 的派生类型”这里有些误解,但我知道你想强调的是“隐式转换导致的耦合”。如果是把两个关联很弱的类硬拉凑到一起做个隐式转换,这的确会产生额外的耦合。但是“额外的耦合”这种问题本质上并不应归咎于一个无辜的语法特性,而应归咎于设计者的缺乏经验及不慎。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/511698

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX