может ли быть реализована идиома безопасной bool без наследования от класса safe_bool? - PullRequest
1 голос
/ 15 октября 2010

Есть ли хитрость для того, чтобы идиома безопасной 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 все равно будет компилироваться, поскольку указатели членов можно сравнивать.Та же реализация, что и выше, но с указателем на функцию-член вместо указателя на переменную-член имеет точно такую ​​же проблему.

Известные реализации, которые работают идеально (как описано, например, здесь , илиsafe_bool, используемый в boost), требует, чтобы тестируемый класс был производным от класса, обеспечивающего фактическую реализацию оператора.

Я на самом деле думаю, что нет никакого способа обойти это, но я не совсем уверен.Я попробовал что-то, что выглядело немного странно, но я подумал, что это может сработать, но это не компилируется вообще.Почему компилятору не разрешено видеть, что оператор возвращает 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

Ответы [ 3 ]

2 голосов
/ 15 октября 2010

Это должно работать:

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) это зависит от того, что оба типа преобразуются в несовместимые типы указателей.

1 голос
/ 15 октября 2010

"Почему компилятору не разрешено видеть"

У меня нет ответа для безопасного bool, но я могу сделать это немного.Это потому, что последовательность конверсий может включать не более 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 должен компилироваться, но не ссылаться.

Я не уверен, что == и!= все ли операторы нужно перегрузить, но есть ли еще что-нибудь, что вы можете сделать с указателем на элемент данных?Это может раздуться.На самом деле это не лучше, чем ответ Барта, я просто упоминаю об этом, потому что ваша первая попытка была близка к работе.

0 голосов
/ 15 октября 2010

РЕДАКТИРОВАТЬ : О! Я не прочитал ваши требования правильно, поэтому ниже не удовлетворяет всем из них.

Это достаточно просто без какого-либо специального базового класса:

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 строк простого кода.

...