为什么某些运算符只能作为成员函数重载,其他作为友元函数,其余的作为两者?

45 c++ class operator-overloading member-functions

为什么某些运算符只能作为成员函数重载,其他作为非成员"自由"函数,其余的作为两者?

这些背后的理由是什么?

如何记住哪些运营商可以超载(成员,免费或两者)?

Die*_*ühl 32

该问题列出了三类运算符.将它们放在一个列表中有助于我理解为什么一些运算符在可以重载的位置受到限制:

  1. 必须作为成员超载的运营商.这些相当少:

    1. 作业operator=().允许非成员分配似乎为操作员劫持任务打开了大门,例如,通过超载不同版本的const资格.鉴于赋值运算符相当基础,似乎是不合需要的.
    2. 函数调用operator()().函数调用和重载规则足够复杂.通过允许非成员函数调用操作符来进一步使规则复杂化似乎是不明智的.
    3. 下标operator[]().使用有趣的索引类型似乎可能会干扰对运营商的访问.虽然劫持重载几乎没有什么危险,但似乎没有太大的收获,但有可能编写高度非显而易见的代码.
    4. 类成员访问operator->().副手我看不到任何重载此操作员非成员的不良滥用.另一方面,我也看不到任何.此外,类成员访问操作符具有相当特殊的规则,并且玩潜在的重载干扰这些似乎是不必要的复杂化.

    尽管可以想象重载这些成员中的每一个都是非成员(特别是下标运算符,它可以在数组/指针上运行,并且这些可以在调用的任何一侧)但是,例如,如果一个赋值可能被劫持,这似乎是令人惊讶的.通过非成员重载,这是一个比成员分配更好的匹配.这些运算符也相当不对称:您通常不希望支持涉及这些运算符的表达式两侧的转换.

    也就是说,例如,对于一个lambda表达式库,如果可以重载所有这些运算符会很好,我不认为有一个固有的技术原因可以防止这些运算符过载.

  2. 必须作为非成员函数重载的运算符.

    1. 用户定义的文字 operator"" name()

    这个算子有点像一个奇怪的球,可能并不是真正的算子.在任何情况下,没有对象可以调用此成员,可以定义成员:用户定义文字的左参数始终是内置类型.

  3. 在问题中没有提到,但也有一些运算符根本不能超载:

    1. 成员选择器 .
    2. 指向成员的对象访问运算符 .*
    3. 范围运算符 ::
    4. 三元运算符 ?:

    这四个运营商被认为太根本,根本无法干预.虽然有一个提议允许operator.()在某些时候超载,但没有强有力的支持这样做(主要的用例是智能参考).虽然确实存在一些可以想象的环境,但也可以使这些运算符超载.

  4. 可以作为成员或非成员超载的运营商.这是大部分运营商:

    1. 前和后递增/ -decrement operator++(),operator--(),operator++(int),operator--(int)
    2. [一元]解除引用 operator*()
    3. [一元]地址 operator&()
    4. [一元]的迹象operator+(),operator-()
    5. 逻辑否定operator!()(或operator not())
    6. 按位反转operator~()(或operator compl())
    7. 攀比operator==(),operator!=(),operator<(),operator>(),operator<=(),和operator>()
    8. 的[二进制]算术operator+(),operator-(),operator*(),operator/(),operator%()
    9. [binary]按位operator&()(或operator bitand()),operator|()(或operator bit_or()),operator^()(或operator xor())
    10. 按位移位operator<<()operator>>()
    11. 逻辑operator||()(或operator or())和operator&&()(或operator and())
    12. 操作/分配operator@=()(@作为合适的操作符号()
    13. 序列operator,()(重载实际上会杀死序列属性!)
    14. 指针指向成员访问 operator->*()
    15. 内存管理operator new(),operator new[](),operator new[](),和operator delete[]()

    作为成员或非成员可能过载的操作员不像其他操作员那样进行基本对象维护.这并不是说它们并不重要.事实上,这个列表包含了一些运营商的地方是相当值得怀疑,他们是否应该是重载(例如,地址的operator&()或通常导致测序,即运营商operator,(),operator||()以及operator&&().

当然,C++标准没有给出为什么事情按照完成方式完成的理由(并且也没有关于这些决策所在的早期记录).最好的理由可以在Bjarne Stroustrup的"C++的设计和演变"中找到.我记得在那里讨论过运营商,但似乎没有可用的电子版本.

总的来说,除了潜在的并发症之外,我认为除了潜在的并发症之外,还有其他限制措施的确有很强的理由.但是,我怀疑这些限制可能会被取消,因为与现有软件的交互必然会以不可预测的方式改变某些程序的含义.


小智 8

理由是它们不是非成员是没有意义的,因为运算符左侧的东西必须是类实例.

例如,假设一个A类

A a1;
..
a1 = 42;
Run Code Online (Sandbox Code Playgroud)

最后一个语句实际上是这样的调用:

a1.operator=(42);
Run Code Online (Sandbox Code Playgroud)

对LHS的事情没有意义.不要成为A的实例,因此该函数必须是成员.

  • "我们必须接受标准所说的" - 当然,但这并不排除寻求理由.通常,委员会出于某种原因做出决定.你已经说过这个被禁止的原因是它"没有意义".相反,比如因为一些委员会成员在醉酒时将其放入标准;-) (4认同)
  • 我可以想到用途.例如,B类理论上可能希望通过重载operator =(A&,B)来改变它分配给A的方式,但B可能由于某种原因不想将转换运算符定义为A(例如因为你不想要发生的其他隐式演员表).这种欲望可能是不明智的,反对普通的做法等等,但我不确定这是荒谬的还是你(还)已经反对它. (2认同)

edu*_*ffy 6

因为您无法修改基本类型的语义.定义如何operator=工作int,如何使用指针或数组访问如何工作是没有意义的.