Что уровень развития о функциях проверить, установлено значение или нет?
Например, приведенный ниже итератор анализирует ячейки.
Некоторые ячейки содержат значение, другие ячейки пусты.
Какой самый удобный способ?
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
...
};
Размышления:
isset()
=> переименован в isSet()
empty()
больше о сборе контейнеров, а не одной ячейке 🙁operator void*
кажется логичным способом, но устарел в потоках C ++ 11explicit operator
является пока не поддерживается (мой код должен быть совместим со старыми компиляторами)Я читаю:
Я впечатлен, что 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» и т. Д.
void*
имеет проблемы, так как это допустимая последовательность преобразования, которая не была предназначена в некоторых случаях. Многие люди используют в C ++ 03 иногда называютбезопасная идиома«где у вас есть локальный тип указателя на функцию-член, который содержит закрытые типы, чтобы никто не мог получить его экземпляр за пределами вашего класса. Однако вы можете вернуть его и, по крайней мере, проверить на true / false.
Когда вы используете C ++ 11, то explicit operator bool
это путь, так как он был изобретен именно для этих случаев.
Большую часть времени не следует использовать неявное преобразование, то есть использование выражений, подобных 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, и они не могут получить ее, потому что она закрыта.
Спасибо за все ваши комментарии / ответы / вклады. Здесь я объединил различные идеи и другие идеи, найденные в сети (я прочитал много документации и исходного кода).
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()
обработка.
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
| }
Некоторые люди думают, что исключение плохо для оптимизации двоичного кода. Однако если 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
}
operator explicit_cast<bool> () const
Пожалуйста, обратитесь к хорошо написано Ответ Луиса.
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.
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;
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();
}
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;
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);
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
bool
идиомаЭто гораздо сложнее, пожалуйста, обратитесь к Wikibooks для исходного кода.
Библиотека:
struct iterator : safe_bool <iterator> //I do not want virtual functions
{
bool boolean_test() const { return ....; }
....
};
Последние STL и Boost предоставляют услуги. Некоторые примеры:
safe_bool
:
Но Мэтью Уилсон говорит в своей книге Несовершенный С ++ тот safe_bool
может привести к штрафам за размер компиляторов, не внедряющих Оптимизация пустой базы. Хотя большинство современных компиляторов делают это, когда дело доходит до одиночного наследования, может быть штраф за размер с множественным наследованием.