为什么在c++中不应重载逗号、地址、逻辑AND和逻辑OR运算符

why comma,address-of, logical AND and logical OR operators should not be overloaded in c++?

本文关键字:AND 逻辑 地址 运算符 OR c++ 重载 为什么      更新时间:2023-10-16

有人能举例说明为什么在c++中重载逗号、地址、逻辑AND和逻辑OR运算符不是一种好的做法吗?

根本原因是这些运算符的重载版本的行为与内置版本不同。这可能会导致(人类)在读/写代码时出现严重的混乱。

  1. 逻辑运算符&&||内置版本表现出短路优化:在像a && b这样的表达式中,首先评估a,并且仅当同时评估trueb时;类似地,在a || b中,首先评估a,并且仅当还评估falseb时。重载运算符&&||不具有短路优化(总是对ab进行求值),并且也没有指定参数的求值顺序。

  2. 逗号运算符内置版本保证按参数出现的顺序计算参数,即在a,b中,先计算a,然后计算b。对于重载的逗号运算符,这种保证将丢失(取而代之的是函数参数机制)。

  3. 运算符的地址当应用于不完整类型的对象时,运算符&的内置地址和重载地址之间可能存在混淆。考虑这个代码

    struct type;            // forward declaration: type is (as of yet) incomplete
    #include <memory>
    void foo(type &obj)     // allowed: take object of incomplete type by reference
    {
      auto ptr1 = &obj;                  // address of obj -- or not?
      auto ptr2 = std::addressof(obj);   // always address of obj    
    }
    // possibly in a different translation unit:
    struct type
    {
      type* operator&() { return nullptr; }
    };
    

    此代码表现出未指定的行为:编译器可以在foo()中实现&运算符的任何一个版本,但编写foo()代码的人无法知道将使用此运算符或哪个运算符。当使用std::addressof作为ptr2时,即使type具有过载的&运算符,也避免了这个问题,因为CCD_23获得了运算符的内建地址的等价值。

如果有人阅读代码并看到if(a && b),他/她会怎么想
如果a和b是像boolint这样的原生类型,那么每个人都知道它是如何工作的。

如果它们是类对象,大多数人会期望该类在应用&&之前提供一个operator bool()或类似的对象来查询它应该是什么布尔。没有人会认为这是一种"奇特"的编写if(a.myMethod(b))的方式,也没有人会检查类定义=>每个人都会误解和/或混淆条件的用途。
tldr,不要使用内置的&&作为不相关方法调用的缩写。

这同样适用于||,

至于地址运算符,除了读取混淆问题外,它在编写代码时也很常见:指针地址通常可以为任何类型的任何变量获取,在这样做之前,没有人会检查类定义。

除了可读性等之外,是否以及按何种顺序评估参数也有所不同:

对于if(a&&b),如果a是false,则b甚至不再被检查,因为整个条件只能为false。如果您有类似if(a && func1()) func2();的东西,其中func2()的返回值很重要,则仅当atrue时执行func1(),并且仅当两者都为真时执行func2()。对于过载的&&func1()总是执行。根据它的作用,这可能不是你想要的。

对于||,它是类似的:如果第一个参数是true,那么第二个参数通常不再被查看,因为整个条件无论如何都是真的。使用重载时,始终对参数进行求值。

一个普通的逗号运算符CCD_ 48保证CCD_;在过载的情况下,它可以是任何顺序。