是否可以在不必从 safe_bool 类派生的情况下实现 safe bool 习惯用法?

发布于 2024-09-27 17:56:19 字数 1492 浏览 8 评论 0原文

是否有一个技巧可以让 safe 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 仍然会编译,因为可以比较成员指针。与上面相同的实现,但使用指向成员函数的指针而不是指向成员变量的指针具有完全相同的问题。

可以完美工作的已知实现(例如此处所述,或者 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

Is there a trick to get the safe bool idiom completely working without having to derive from a class that does the actual implementation?

With 'completely working', I mean the class having an operator allowing it to be tested as if it were a boolean but in a safe way:

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;

when using this implementation for example

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 ); }
}

it's almost fine, except a == b will still compile, since member pointers can be compared. The same implementation as above, but with pointer to member function instead of pointer to member variable has exactly the same problem.

Known implementations that do work perfectly (as described here for example, or the safe_bool used in boost) require that the testable class derive from a class providing the actual operator implementation.

I actually think there is no way around it, but'm not entirely sure. I tried something that looked a bit fischy but I thought it might work, yet is doesn't compile at all. Why is the compiler not allowed to see that the operator returns a safe_bool_thing, which in turn can be converted to bool() and hence be tested?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

绮筵 2024-10-04 17:56:19

这应该可行:

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) 情况,它取决于两种类型都转换为不兼容的指针类型的事实。

This should work:

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); }
};

For blocking the if (a == b) case, it depends on the fact that both types convert to incompatible pointer types.

鲸落 2024-10-04 17:56:19

“为什么编译器不允许看到”

我没有安全布尔的答案,但我可以做到这一点。这是因为一个转换序列最多可以包含 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 &),它是私有的。

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

我不确定 ==!= 是否都是您需要重载的运算符,但是,您还可以使用指向数据成员的指针做任何其他事情吗?这可能会变得臃肿。这并不比巴特的答案更好,我只是提到它,因为你的第一次尝试已经接近成功。

"Why is the compiler not allowed to see"

I don't have an answer for safe bool, but I can do this bit. It's because a conversion sequence can include at most 1 user-defined conversion (13.3.3.1.2).

As for why that is - I think someone decided that it would be too hard to figure out implicit conversions if they could have arbitrarily many user-defined conversions. The difficulty it introduces is that you can't write a class with a conversion, that "behaves like built-in types". If you write a class which, used idiomatically, "spends" the one user-defined conversion, then users of that class don't have it to "spend".

Not that you could exactly match the conversion behaviour of built-in types anyway, since in general there's no way to specify the rank of your conversion to match the rank of the conversion of the type you're imitating.

Edit: A slight modification of your first version:

#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 will not compile, because function overload resolution ignores accessibility. Accessibility is only tested once the correct function is chosen. In this case the correct function is MyTestableClass::operator==(const MyTestableClass &), which is private.

Inside the class, a == b should compile but not link.

I'm not sure if == and != are all the operators you need to overload, though, is there anything else you can do with a pointer to data member? This could get bloated. It's no better really than Bart's answer, I just mention it because your first attempt was close to working.

高速公鹿 2024-10-04 17:56:19

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

这很容易,不需要任何特殊的基类:

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 行简单代码即可实现它。

EDIT: Oh! I did not read your requirements correctly, so the below does not satisfy all of them.

It is easy enough without any special base class:

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;
   }
};

So, given suitable member variable of member function, you can impement it in just 5 lines of simple code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文