4

来自@Xeo 的优秀c++-faq问题:安全布尔成语在 C++11 中是否已过时?我了解到不再需要安全布尔惯用语,因为在 C++03 中需要安全布尔的上下文中,将自动调用explicit用户定义的转换为。bool

&&但是,重载,||和等运算符的能力!似乎可以规避这一点。

operator!除了提供转换之外必要的情况bool很少见,例如operator&&and 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;
4

1 回答 1

4

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

1 && 运算符从左到右分组。操作数都根据上下文转换为 bool 类型(第 4 条)

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


重载运算符是常规函数调用,因此不会发生上下文转换。确保始终通过重载解析选择重载的布尔运算符,并且一切顺利。例如,这是忽略左值:

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

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

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

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

于 2011-09-25T03:56:46.770 回答