【问题标题】:can the safe bool idiom be implemented without having to derive from a safe_bool class?可以实现安全布尔成语而不必从 safe_bool 类派生吗?
【发布时间】:2026-01-03 18:50:01
【问题描述】:

是否有一个技巧可以让安全的 bool 习惯用法完全正常工作,而不必从执行实际实现的类派生?

对于“完全正常工作”,我的意思是类有一个运算符,允许它像一个布尔值一样被测试,但以一种安全的方式:

MyTestableClass a;
MyOtherTestableClass b;
  //this has to work
if( a );
if( b );
  //this must not compile
if( a == b );
if( a < b );
int i = a;
i += b;

例如使用此实现时

struct safe_bool_thing
{
  int b;
};
typedef int safe_bool_thing::* bool_type;
safe_bool( const bool b ) { return b ? &safe_bool_thing::b : 0; }

class MyTestableClass
{
  operator bool_type () const { return safe_bool( someCondition ); }
}

几乎没问题,除了 a == b 仍然可以编译,因为可以比较成员指针。与上面相同的实现,但是使用指向成员函数的指针而不是指向成员变量的指针有完全相同的问题。

可以完美运行的已知实现(例如,here 所述,或 boost 中使用的 safe_bool)要求可测试类派生自提供实际运算符实现的类。

我实际上认为没有办法绕过它,但并不完全确定。我尝试了一些看起来有点古怪的东西,但我认为它可能会起作用,但根本无法编译。为什么不允许编译器看到操作符返回一个safe_bool_thing,然后又可以转换为bool(),从而进行测试?

struct safe_bool_thing
{
  explicit safe_bool_thing( const bool bVal ) : b( bVal ) {}
  operator bool () const { return b; }
private:
  const bool b;
  safe_bool_thing& operator = ( const safe_bool_thing& );
  bool operator == ( const safe_bool_thing& );
  bool operator != ( const safe_bool_thing& );
};

class MyTestableClass
{
  operator safe_bool_thing () const { return safe_bool_thing( someCondition ); }
};

MyTestableClass a;
if( a ); //conditional expression of type 'MyTestableClass' is illegal

【问题讨论】:

  • a == b 应该编译...对于两个布尔值非常有意义。你想要一个安全的布尔值还是绑定的、阻塞的和链式的布尔值? ;-)
  • 对布尔值有意义,是的,但对两个完全不相关的类没有意义!
  • 出于兴趣,如果您制作第一个版本,然后将 MyTestableClass::operator== 设为私有会怎样?这有什么问题吗,不应该阻止a==b 进行任何转换吗?在实践中,从safe_bool 的助手继承通常是一件的事情,所以我对所有标准实现都这样做并不感到惊讶。
  • 取决于您的意思是哪个 operator== .. 如果您使用 operator == ( const MyTestableClass& ),它不会做任何偏离路线的事情。如果您使用 bool operator == ( const bool_type& ),那么它确实给出了我想要的行为。这样写有点奇怪,但它确实有效。也许您可以将其发布为答案?
  • 我不认为有任何“当然”,我已经编辑了我的答案:-)

标签: c++ operators boolean


【解决方案1】:

这应该可行:

class MyTestableClass
{
private:
  void non_comparable_type() {}
public:
  typedef void (MyTestableClass::* bool_type)();

  operator bool_type () const { return (someCondition ? &MyTestableClass::non_comparable_type : 0); }
};

class MyOtherTestableClass
{
private:
  void non_comparable_type() {}
public:
  typedef void (MyOtherTestableClass::* bool_type)();

  operator bool_type () const { return (someCondition ? &MyOtherTestableClass::non_comparable_type : 0); }
};

对于阻止if (a == b) 的情况,这取决于两种类型都转换为不兼容的指针类型这一事实。

【讨论】:

  • 哈!这个确实有效。只是有不能完全可重用的缺点,但我猜这就是你在不派生时得到的。
  • 是的。这里继承的主要原因是实现的重用。
【解决方案2】:

"为什么编译器不允许看到"

我没有安全布尔的答案,但我可以做到这一点。这是因为一个转换序列最多可以包含1个用户定义的转换(13.3.3.1.2)。

至于为什么 是 - 我认为有人认为如果他们可以有任意多个用户定义的转换,就很难弄清楚隐式转换。它引入的困难是你不能编写一个带有转换的类,它“表现得像内置类型”。如果您编写的类按照惯用方式“花费”了一次用户定义的转换,那么该类的用户就没有它来“花费”。

并不是说您无论如何都可以完全匹配内置类型的转换行为,因为通常没有办法指定您的转换排名以匹配您正在模仿的类型的转换排名。

编辑:对您的第一个版本稍作修改:

#define someCondition true

struct safe_bool_thing
{
    int b;
};
typedef int safe_bool_thing::* bool_type;
bool_type safe_bool( const bool b ) { return b ? &safe_bool_thing::b : 0; }

class MyTestableClass
{
public:
    operator bool_type () const { return safe_bool( someCondition ); }
private:
    bool operator==(const MyTestableClass &rhs);
    bool operator!=(const MyTestableClass &rhs);
};

int main() {
    MyTestableClass a;
    MyTestableClass b;
    a == b;
}

a == b 不会编译,因为函数重载决议忽略了可访问性。仅在选择正确的功能后才测试可访问性。在这种情况下,正确的函数是 MyTestableClass::operator==(const MyTestableClass &amp;),它是私有的。

在类内部,a == b 应该编译但不能链接。

我不确定==!= 是否都是您需要重载的运算符,但是,您还可以对指向数据成员的指针做些什么吗?这可能会变得臃肿。这并不比巴特的回答好,我只是提到它是因为你的第一次尝试接近工作。

【讨论】:

  • a == b 的修改确实可以,但如果 b 是另一种也具有运算符 bool_type 的类型,则不能;最后一个正是我想要避免的。如果 MyTestableClass 和 MyOtherTestableClass 都将 operator == ( const bool_type& ) 声明为私有,那么它是否有效。
【解决方案3】:

编辑:哦!我没有正确阅读您的要求,因此以下内容不能满足所有要求。

没有任何特殊的基类就很容易了:

struct MyClass
{
   int some_function () const;

   typedef int (MyClass:: * unspec_bool_type) () const;
   operator unspec_bool_type () const
   {
       return some_condition ? &MyClass::some_function : 0;
   }
};

所以,给定合适的成员函数成员变量,只需5行简单的代码即可实现。

【讨论】:

  • 是的,这个解决方案基本上就是我所说的'与上面相同的实现,但使用指向成员函数的指针而不是指向成员变量的指针'..
最近更新 更多