为什么重载复合赋值运算符可以是非成员函数?

3

我最近才发现,复合赋值运算符(如operator+=operator-=)可以在类作用域之外进行重载。

比如:

class X { }; // A third party class

// The following function is legal:
X& operator+=(X& lhs, const X& rhs) {
    // ...
}

显然,非成员函数无法访问X的私有接口,因此这里没有封装问题。但它确实使它看起来像是X自己的接口的一部分,而事实上并不是。

我认为这可能会导致严重的代码滥用和混乱的行为。想象一下,有人认为使用它与标准容器(如std::vector或std::map)进行一些“聪明的黑客攻击”会很好。

这特别奇怪,因为我认为operator[]和operator->等其他运算符不能是非成员函数,原因就是这个。

那么为什么允许这样做呢?


3
我没有看到任何问题。 - Columbo
3个回答

5

那么为什么允许这样做呢?

你在要求我们读懂Stroustrop的想法,这是不可能的。
但C++的一个普遍原则是不限制开发人员(我们不提供一组被削弱的工具来安全地玩耍。我们提供全套的锋利锯和旋转链枷)。

似乎这可能会导致一些严重的代码滥用和混乱的行为。想象一下,有人认为使用它与标准容器如std::vector或std::map进行一些“聪明的黑客”将是很好的选择。

是的,它可能会。
当您滥用运算符重载时,可能会导致一些危险和致命的事情。最好不要这样做(特别是对于其他人或标准类)。

但是它可以提供一些潜在有用的情况(当您小心时)。这在构建数值“类型”类时特别有用,因为它有助于自动转换,使代码更加自然易读。


4
考虑一下,如果库A定义了一个对象(比如矩阵对象),库B用一些类似的对象(比如向量)扩展了库A。
库B想要定义将矩阵与向量链接起来的运算符(例如乘法)。但是如果这些运算符重载必须在原始对象中完成,编写者将陷入困境,用户将处于奇怪的情况:他们可以做向量*矩阵,但不能做矩阵*向量。
特别看一下+=和-=,因为你提到了它们,考虑一个1-by-n矩阵,它实际上是一个向量。我们现在想要执行matrix += vector。
允许在外部定义它们避免了这个问题。
引用:
“我觉得这可能会导致严重的代码滥用和混乱的行为。”
非常正确。但不要忘记这是一种内置预处理器的语言。如果其他开发人员想要使您混淆,他们已经拥有更强大的工具可用。

0

原因之一是它们可能并不总是适用于类。例如,一个类型安全的标志集可以被实现为一个带有重载运算符的枚举,包括复合赋值运算符:

enum Flags : unsigned {
    None   = 0   ,
    Hot    = 1<<0,
    Crazy  = 1<<1,
    Single = 1<<2
};

constexpr Flags operator | (Flags L, Flags R) {
    return (Flags)((unsigned)L | (unsigned)R);
}

// there's nothing this can be a member *of*.
Flags & operator |= (Flags &L, Flags R) {
    return (L = L | R);
}

// etc...

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