运算符应该声明为非成员、非模板友元吗?

24
考虑这个问题,它是关于以下代码无法编译的问题:
std::vector<int> a, b;
std::cout << (std::ref(a) < std::ref(b));

这段代码无法编译,因为vector比较运算符是非成员函数模板,而隐式转换不被允许考虑。然而,如果将这些运算符改为非成员非模板的friend函数:

template <class T, class Allocator = std::allocator<T>>
class vector {
    // ...

    friend bool operator<(const vector& lhs, const vector& rhs) {
        // impl details
    }
};

那么,如果采用这个版本的operator<,ADL将会找到它并选择它作为最佳可行重载,原始示例将会编译。鉴于此,我们是否有理由更喜欢当前所拥有的非成员函数模板,或者应该将其视为标准中的缺陷?


在一些地方,operator<< 和流也存在类似的问题:这确实引起了一些问题。我可以编写一个 template<class C> operator<<(std::basic_ostream<C>&,std::basic_string<C>&) 而不依赖于两者之一的更多声明。"Koenig 操作符" 至少需要其中一个(哪个?), 它是否需要两个都有呢?(这是离题的,因为它涉及到两种类型的操作符,而不是像上面那样只有一个)。 - Yakk - Adam Nevraumont
3
我不明白为什么你会写std::ref(a) < std::ref(b)。对我来说,这似乎是违反直觉的,因为ref返回的是完全独立的类类型。 - Columbo
@Columbo 它适用于 int。此外,我不知道为什么你会做很多事情 - 这并不意味着语言不允许它。此外,这将使您的带有 operator std::string() 的类可以与 string 可比较的东西进行比较。 - Barry
4
如我在这个相关问题中所评论的,运算符通常不需要特殊访问权限。类似于对operator()的支持,通过向reference_wrapper添加一个包装器运算符,就可以支持ref(a) < ref(b)。此外,我不确定依赖隐式转换来应用运算符是否是一个好主意。 - dyp
1个回答

1
鉴于此,我们目前拥有的非成员函数模板是否更好,还是应该将其视为标准中的缺陷?原因在于ADL能否找到合适的函数。当这种搜索需要从给定对象的类型中提取替换的模板参数,然后将它们多次替换到函数模板的模板参数中时,由于在一般情况下没有理由优先选择一种模板参数绑定方式而不是其他方式,因此ADL无法做到这一点。定义在该模板的命名空间范围内但仍为非成员函数模板(由于friend)的函数模板排除了这种不确定性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接