isSet () или оператор void * () или явный оператор bool () или что-то еще?

Что уровень развития о функциях проверить, установлено значение или нет?

Например, приведенный ниже итератор анализирует ячейки.
Некоторые ячейки содержат значение, другие ячейки пусты.

Какой самый удобный способ?

struct iterator
{                                  //usage:
bool isset() const               // if (it.isset())
bool isSet() const               // if (it.isSet())
bool empty() const               // if (it.empty())

bool is_set()   const            // if (it.is_set())
bool is_valid() const            // if (it.is_valid())

operator void*() const;          // if (it)

explicit operator bool() const;  // if ((bool)it) or if(it) //thanks @stijn
operator          bool() const;  // if (it) //why not implicit conversion?

bool operator!() const;          // if (!!it)

//throwing exception as pointed out by @MatthieuM
Type get() { if (isSet()) return value_; else throw; }
//usage:
//     try {                    // if (it.isSet()) {
//        Type x = it.get();    //    Type x = it.get();
//     }                        // }
//     catch (...) {            // else {
//        //empty               //    //empty
//     }                        // }

//please feel free to propose something different
...
};

Размышления:

  1. мой босс не понимает isset() => переименован в isSet()
  2. empty() больше о сборе контейнеров, а не одной ячейке 🙁
  3. operator void* кажется логичным способом, но устарел в потоках C ++ 11
  4. explicit operator является пока не поддерживается (мой код должен быть совместим со старыми компиляторами)

Я читаю:

8

Решение

Я впечатлен, что explicit_cast<T> от Несовершенный C ++: практические решения […] не было упомянуто Концепция очень проста — вы устанавливаете псевдоключевое слово, которое на самом деле представляет собой шаблонный класс, реализующий нужное преобразование. Я использовал это в моем собственная библиотека C ++ backports без каких-либо важных вопросов.

class MyClass {
...implementation
operator explicit_cast<bool> () const {
(return a bool somehow)
}
};

Вы используете псевдо-ключевое слово так, как вы ожидаете, что оно будет работать:

MyClass value;
...
if ( explicit_cast<bool>(myobject) )  {
do something
} else {
do something else
}
...

Самое приятное то, что это решение может быть идеально сопоставлено с нативным explicit operator в C ++ 11, что приводит к практически нулевым издержкам и нативному синтаксису. В результате это также является более общим, чем попытка выяснить, вызывать ли «isset», «is_set», «is_Set», «isSet» и т. Д.

2

Другие решения

void* имеет проблемы, так как это допустимая последовательность преобразования, которая не была предназначена в некоторых случаях. Многие люди используют в C ++ 03 иногда называютбезопасная идиома«где у вас есть локальный тип указателя на функцию-член, который содержит закрытые типы, чтобы никто не мог получить его экземпляр за пределами вашего класса. Однако вы можете вернуть его и, по крайней мере, проверить на true / false.

Когда вы используете C ++ 11, то explicit operator bool это путь, так как он был изобретен именно для этих случаев.

6

Большую часть времени не следует использовать неявное преобразование, то есть использование выражений, подобных operator bool() в вашем коде.

Когда вы хотите иметь возможность использовать экземпляры вашего класса в if В этом случае вы часто будете создавать неявное преобразование, но для сигнатуры прототипа функции-члена, которую вы будете указывать на неактивную частную функцию или на NULL в зависимости от состояния.

Вы также будете часто перегружать bool operator!() const для вашего класса. Поскольку при этом будет использоваться та же логика, что и при неявном преобразовании, вы часто будете реализовывать одно в терминах другого.

Что-то вроде:

private:
struct MyPrivateType {};
void MyPrivateFunc( MyPrivateType ) {}
public:
typedef void (&iterator::*)( MyPrivateType ) bool_func;

operator bool_func() const
{
return operator!() ? static_cast<bool_func>(0) : MyPrivateFunc;
}

Никто не может вызвать функцию, которую вы возвращаете из указателя, потому что она требует MyPrivateType, и они не могут получить ее, потому что она закрыта.

1

Спасибо за все ваши комментарии / ответы / вклады. Здесь я объединил различные идеи и другие идеи, найденные в сети (я прочитал много документации и исходного кода).


1. bool isSet() как указано @j_random_hacker

Самый логичный способ. И исходный код (библиотека и приложение) понятны начинающим. И это соответствует Поцелуй принцип. Более того, это переносимо на другие языки программирования, такие как Java …

Library:               |    Application:
|
struct iterator        |
{                      |
bool isSet() const   |   if (it.isSet())
{                    |   {
return p;          |       int v = it.get();
}                    |       //get() may also call isSet()
|
int get() const      |       //continue processing
{                    |   }
return *p;        |   else //value is not set
}                    |   {
|       //do something else
int* p;              |   }
};                     |

Если функция get() не проверяет isSet() и разработчик (приложения) забывает звонить isSet() (до get()) тогда код приложения может дать сбой (ошибка сегментации).

С другой стороны, если get() вызовы функций isSet() следовательно isSet() обработка выполняется дважды. Тем не менее, последние компиляторы должны избегать такого второго ненужного isSet() обработка.

2. Вернуть значение флага или значение по умолчанию, предложенное одним из моих коллег.

Library:               |    Application:
|
struct iterator        |   int i = it.get()
{                      |   if (i >= 0)
int get() const      |   {
{                    |     unsigned short v = i;
if(p) return *p;   |
else  return -1;   |     //continue processing
}                    |   }
|   else //value is not set
unsigned short* p;   |   {
};                     |     //do something else
|   }

3. Бросать исключение, как указано @Матье М

Некоторые люди думают, что исключение плохо для оптимизации двоичного кода. Однако если throw exception это означает, что лучшие компиляторы могут оптимизировать двоичный код, и лучше, чем
Кроме того, это решение может позволить лучший оптимизированный двоичный код, потому что isSet() называется дважды. Но это зависит от возможностей оптимизации компилятора.

Библиотека:

struct iterator
{
bool get() const
{
if (isSet()) return *p;
else throw;
}

private:
bool isSet() const  { return ....; }

....
};

Заявка:

int value;
try
{
value = it.get();
}
catch (...)
{
value = 0; // default value
}

4. Использование operator explicit_cast<bool> () const

Пожалуйста, обратитесь к хорошо написано Ответ Луиса.

5. Использование operator писать элегантно if(it)

Это решение может быть хорошо реализовано с использованием явные операторы преобразования, представленные в C ++ 11.

Библиотека:

struct iterator
{
explicit operator bool() const  { return ....; }

....
};

Заявка:

int value;
if (it)      //very elegant C++ fashion
{
value = it.get();
}
else
{
value = 0; // default value
}

Однако мы все еще находимся в 2012 году, и текущий исходный код должен быть совместим с компиляторами без поддержки операторы явного преобразования. На этих компиляторах различные возможности были реализованы годы за годами. Я представляю все это в следующей главе.


Исходный код этой главы вдохновлен из книги Больше идиом C ++ написано Бьярне Страуструп в 2004 году, в частности, раздел Безопасная идиома как указано @PlasmaHH.

1. неявный operator bool

когда explicit недоступен, мы могли бы просто использовать оператор неявного преобразования.

Библиотека:

struct iterator
{
operator bool() const  { return ....; } //implicit conversion

....
};

Заявка:

int value;
if (it)      //this works very well!
{
value = it.get();
}
else
{
value = 0; // default value
}

// But these other instructions are also correct :(
int integer = it;   //convert it to bool, then convert bool to int
if (-6.7 < it)      //.................., then convert bool to double, and compare
it << 1;

2. operator!

Это решение используется в boost::thread (v1.51) в качестве обходного пути explicit operator bool() за unique_lock, shared_lock, upgrade_lock а также upgrade_to_unique_lock.

Библиотека:

struct iterator
{
bool operator!() const  { return ....; }

....
};

Заявка:

int value;

if (!!it)      // !! looks strange for many developers
{
value = it.get();
}
else
{
value = 0; // default value
}

if (it)    //ERROR: could not convert ‘it’ from ‘iterator’ to ‘bool’
{
value = it.get();
}

3. operator void*

Это решение, используемое потоками STL. Например, обратитесь к файлу бит / basic_ios.h (std::basic_ios).

Библиотека:

struct iterator
{
operator void*() const  { return ....; }

....
};

Заявка:

int value;
if (it)      //this works very well!
{
value = it.get();
}
else
{
value = 0; // default value
}

// But these other instructions are also correct :(
delete it;  //just a warning: deleting 'void*' is undefined
if (it > std::cin) //both are converted to void*
void* r = it;

4. неявное преобразование в неопределенное вложенное class

Это решение было предложено Дон Бокс в 1996 году.

Библиотека:

struct iterator
{
private:
class nested; //just a forward declaration (no definition)
int* v_;
public:
operator nested*() const  { return v_ ? (nested*)this : 0; }
};

Заявка:

int value;
if (it)      //this works very well!
{
value = it.get();
}
else
{
value = 0; // default value
}

// But these other instructions are also correct :(
iterator it2;
if (it < it2)
int i = (it == it2);

5. Сейф bool идиома, представленная @Дойная корова

Бьярне Страуструп предложил оптимальное решение без недостатков. Ниже приведена упрощенная версия.

Библиотека:

struct iterator
{
private:
typedef bool (iterator::*bool_type)() const;
bool private_() const {}
int* v_;

public:
operator bool_type() const  { return v_ ? &iterator::private_ : 0; }
};

//forbids it1 == it2
template <typename T>
bool operator == (const iterator& it,const T& t) { return it.private_(); }

//forbids it1 != it2
template <typename T>
bool operator != (const iterator& it,const T& t) { return ! (it == t); }

Заявка:

int value;
if (it)      //this works very well!
{
value = it.get();
}
else
{
value = 0; // default value
}

// All other instructions fail to compile
iterator it2;
if (it >  it2)  ;  //ERROR:  no match for ‘operator>’ in ‘it > it2’
if (it == it2)  ;  //ERROR: ‘bool iterator::private_() const’ is private
if (it != it2)  ;  //same error

6. Многоразовый сейф bool идиома

Это гораздо сложнее, пожалуйста, обратитесь к Wikibooks для исходного кода.

Библиотека:

struct iterator : safe_bool <iterator> //I do not want virtual functions
{
bool boolean_test() const  { return ....; }

....
};

Последние STL и Boost предоставляют услуги. Некоторые примеры:

  • GNU STL -> см. Файлы tr1 / functions и exception_ptr.h
  • Каждый компонент Boost использует свой собственный safe_bool:
    • Spirit -> см. Файлы spirit / include / classic_safe_bool.hpp и spirit / home / classic / core / safe_bool.hpp
    • IOstream -> см. Файл iostreams / device / mapped_file.hpp
    • Параметр -> параметр / aux_ / Maybe.hpp
    • Необязательный
    • функция
    • Спектр
    • Логика (трибула)

Но Мэтью Уилсон говорит в своей книге Несовершенный С ++ тот safe_bool может привести к штрафам за размер компиляторов, не внедряющих Оптимизация пустой базы. Хотя большинство современных компиляторов делают это, когда дело доходит до одиночного наследования, может быть штраф за размер с множественным наследованием.

0