懒惰的,过载的C++ &&运算符?

8

我想实现自己的布尔类,但是无法复制本地的&&语义。以下编写的代码演示了这个问题:



    #include <iostream>>

    class MyBool {
    public:
        bool theValue;
        MyBool() {}
        MyBool(bool aBool) {theValue = aBool;}
        MyBool operator&& (MyBool aBool) {return theValue && aBool.theValue;}
    };

    bool f1() {std::cout << "   First\n"; return false;}
    bool f2() {std::cout << "   Second\n"; return false;}

    int main(int argc, char** argv) {
        std::cout << "Native &&\n";
        f1() && f2();
        std::cout << "Overloaded &&\n";
        MyBool(f1()) && MyBool(f2());
        return 0;
}


当编译并运行时,结果如下:
Native && First Overloaded && Second First
换句话说,对于布尔值,&&是惰性的(任何C++程序员都会预期),但重载的&&不是(至少这位C++程序员没有预料到)。
有没有办法使重载的&&变成惰性的?我可以找到各种全面的惰性求值方案,以提供类似Haskell的功能,但它们似乎对我的用例来说过于复杂。

3
“By lazy you mean short circuit.” 的意思是“通过惰性求值实现短路计算”。重载运算符并不具有短路计算的功能。 - Rapptz
这个问题正是为什么建议不要重载 && 运算符的原因。 - Martin York
请注意,重载运算符无法短路的原因是将MyBool aBool作为参数传递给它。因此,在实现operator&&有机会运行之前,需要计算MyBool(f2())。理论上,您可以编写一个模板operator&&重载,其RHS是返回MyBool的函数对象。然后仅在LHS评估为true时调用该函数对象。然后在C++11中,调用者可以编写一个lambda表达式:MyBool(f1()) && []() { return MyBool(f2()); }。但在实践中,不要这样做——当您必须在每次调用时显式指定它时,惰性求值非常丑陋。 - Steve Jessop
4个回答

17

你不应该重载 bool operator&&,因为你会失去短路求值的功能,就像你已经发现的那样。

正确的方法是给你的类一个 bool 转换运算符。

class MyBool {
 public:
  bool theValue;
  MyBool() {}
  MyBool(bool aBool) : theValue(aBool) {}
  explicit operator bool() { return theValue; }
};

请注意,显式转换运算符需要符合 C++11 标准。如果您没有此标准,请查看安全布尔技巧


1
+1,但需要注意的是explicit operator是C++11的特性,而C++03需要使用Safe Bool习惯用法。 - greyfade

6
有没有一种方法可以使重载的 && 成为懒加载?
没有。

2

使用表达式模板习惯用法,您几乎可以让任何东西都进行惰性求值,包括但不限于其内置版本具有短路功能的运算符。但是,对于这种情况,这比您需要的工作要多得多,因为那样您的MyBool类将需要更多的代码。


“你可以使几乎任何东西惰性求值” - 这是误导性的,因为这种方法只适用于合作的用户定义类型。例如,对于任何 T* p;myBool && ++p,你不能短路增量操作。 - Tony Delroy

0

如果你真的想要短路计算并愿意牺牲运算符语法,你可以将你的operator&&方法重命名为_and,定义一个AND()宏,并写成AND(x,y)而不是x&&y

#define AND(x,y) (x._and(x.theValue ? y : MyBool(false)))

通过一些宏技巧,您可以让AND()接受可变数量的参数。

这里的_and()方法不是用于“公开”使用的,但必须声明为public,因为您无法将宏设置为friend

对于像MyBool类这样简单的东西,这可能是不必要的。但是,如果您需要使您的operator&&具有特殊的副作用,例如在this上更新某些状态,则可以完成此操作。


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