“&&”和“||”运算符的“上下文转换”如何与用户定义的运算符重载一起工作?

时间:2011-09-25 01:12:52

标签: c++ operator-overloading c++11 safe-bool-idiom

来自@ Xeo的优秀c++-faq问题:Is the safe-bool idiom obsolete in C++11?我了解到不再需要安全的bool成语,因为explicit用户定义的转换为bool将是在C ++ 03中需要安全bool的上下文中自动调用。

然而,重载&&||!等运算符的能力似乎可以避免这种情况。

除了提供转换为operator!之外,bool所需的情况很少见,operator&&operator||也是如此,但C ++表达式树实现(用于延迟执行和符号数学技巧)确实需要覆盖这些。

在调用用户定义的运算符时是否会发生“上下文转换”?需要使用哪种SFINAE咒语来确保operator&&operator||的定义在实现“安全布尔”的类型和“上下文转换”的类型中都能正常工作?


澄清,给出:

class uses_safe_bool
{
    void f() {};
    typedef void (uses_safe_bool::* safe_bool)();

public:
    operator safe_bool() const { return (rand() & 1)? &uses_safe_bool::f: 0; }
};

class uses_explicit_bool
{
public:
    explicit operator bool() const { return rand() & 1; }
};

template<typename T>
class deferred_expression
{
    // Not convertible to bool
public:
    T evaluate() const;
};

operator||需要哪些签名才能使以下表达式全部有效:

deferred_expression<bool> db;
uses_safe_bool sb;
uses_explicit_bool eb;
int i;

auto test1 = sb || db;
auto test2 = eb || db;
auto test3 = true || db;
auto test4 = false || db;
auto test5 = i || db;

这些使用不同的重载:

auto test6 = db || db;

deferred_expression<int> di;
auto test7 = di || db;

以下内容在编译时被拒绝:

std::string s;
auto test7 = s || db;

std::vector<int> v;
auto test8 = v || db;

deferred_expression<std::string> ds;
auto test9 = ds || db;

1 个答案:

答案 0 :(得分:4)

对于C ++ 03(safe-bool成语)和C ++ 11(显式转换运算符),规则是相同的:不要为此重载布尔运算符(以免丢失短路行为,加上默认工作就好了)。后者将起作用,因为内置布尔运算符的操作数有资格进行上下文转换,例如来自n3290的&&,5.14逻辑AND运算符[expr.log.and]:

  

1&amp;&amp;操作员组从左到右。 操作数在上下文中都转换为bool类型(第4条)

(强调我的,其他运营商的类似文本)


重载运算符是常规函数调用,因此不会发生上下文转换。确保你的重载布尔运算符始终通过重载决策选择,你很高兴。例如,这忽略了左值:

struct evil {
    explicit operator bool() const;
};
void operator||(evil&&, evil&&);

evil e;
// built-in operator||
e || e;

// overloaded operator||
evil() || evil()

请注意,当任何一个操作数类型属于类类型时,将通过ADL选择template<typename Lhs, typename Rhs> void operator||(Lhs&&, Rhs&&);,而不管 cv -qualifiers和value-category。