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

4
从@Xeo的优秀c++-faq问题中得知:C++11中safe bool习惯用法已过时吗?我了解到,因为在C++03中需要safe bool的上下文中将自动调用显式的用户定义转换为bool,所以不再需要safe 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个回答

4
C++03(安全bool惯用法)和C++11(显式转换运算符)的规则相同:不要为此重载布尔运算符(以免失去短路行为,而且默认值可以正常工作)。后者可以工作是因为内置布尔运算符的操作数有资格进行上下文转换,例如来自n3290,5.14逻辑AND运算符[expr.log.and]:

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

(强调我的,其他运算符类似的文本)


重载运算符是常规函数调用,因此不会发生上下文转换。确保您的重载布尔运算符始终通过重载决议选择,然后就可以开始了。例如,这忽略了lvalue:
struct evil {
    explicit operator bool() const;
};
void operator||(evil&&, evil&&);

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

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

请注意,当任一操作数类型为类类型时,不论cv限定符和值类别如何,通过ADL选择template<typename Lhs, typename Rhs> void operator||(Lhs&&, Rhs&&);

但如果运算符被重载了会发生什么呢?不要回避这个问题,有一些情况(正如问题中所述)需要进行运算符重载。 - Ben Voigt
@Ben 我添加的这一部分是否涵盖了你想要知道的内容? - Luc Danton
部分地。 对于EDSL类型,人们可能不会提供bool类型的转换,但是人们可能会在同一表达式中使用EDSL类型和另一种类型。 - Ben Voigt
但是,使用新的“显式”范例,EDSL类型中的转换构造函数从bool转换将不会是一个候选项。 - Ben Voigt
@BenVoigt 这不是重点。我的观点是,EDSL已经意识到甚至更“危险”的转换已经存在。因此,引入新的转换不会破坏良好设计的EDSL。(explicit是一个关键字,而不是一种范例。) - Luc Danton
显示剩余3条评论

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