Вы пишете новую восхитительную библиотеку сериализации в JSON. Для этого у вас уже написано много своих версий функции stringify
для поддерживаемых JSON типов. Их там немного...
И вот у вас есть
auto stringify(bool b) -> std::string_view {
return b ? "true" : "false";
}
auto stringify(std::string_view s) -> std::string_view {
return s;
}
Выглядит хорошо и логично.
Вы тестируете эти функции
int main() {
std::cout << stringify(true) << "\n";
std::cout << stringify("string") << "\n";
}
true
true
Удивлены? Но тут нет ничего удивительного! Просто строковый литерал, который имеет тип const char[7]
неявно приводится к const char*
, который неявно приводится к bool
. А поскольку это все built-in преобразования они имеют приоритет перед user-defined преобразованием к std::string_view
через его конструктор.
C неявным приведением указателей к bool
есть еще известный дефект в инициализаторе через фигурные скобки
bool array[5] = {true, false, true, false, true};
std::vector<bool> vector {array, array + 5};
std::cout << vector.size() << "\n";
Будет выведено 2, а не 5. Потому что указатели неявно приводятся к bool
!
Дефект кое-как исправили в C++20. Теперь Clang отказывается это компилировать, а GCC просто выдает предупреждение.
<source>:9:32: error: type 'bool[5]' cannot be narrowed to 'bool' in initializer list [-Wc++11-narrowing]
9 | std::vector<bool> vector { array, array + 5};
| ^~~~~
<source>:9:32: note: insert an explicit cast to silence this issue
9 | std::vector<bool> vector { array, array + 5};
|
А вы знаете как определить для вашего типа все возможные арифметические операторы и операторы сравнения разом? Нужно всего лишь определить неявный оператор приведения к bool
, конечно же!
struct OptionalPositive {
int x;
operator bool() const {
return x >= 0;
}
};
int main() {
std::cout << 5 + OptionalPositive { 5 };
std::cout << (5 < OptionalPositive { 5 });
std::cout << (5 == OptionalPositive { 5 });
std::cout << (5 * OptionalPositive { 5 });
}
Это компилируется и выдает результат 6005
. Потому как выполняется user-defined неявное приведенине к bool
, который далее неявно приводится к int
. Все правильно.
Последние версии Clang хотя бы вывают частично предупреждения
<source>:13:21: warning: result of comparison of constant 5 with expression of type 'bool' is always false [-Wtautological-constant-out-of-range-compare]
13 | std::cout << (5 < OptionalPositive { 5 });
| ~ ^ ~~~~~~~~~~~~~~~~~~~~~~
<source>:14:21: warning: result of comparison of constant 5 with expression of type 'bool' is always false [-Wtautological-constant-out-of-range-compare]
14 | std::cout << (5 == OptionalPositive { 5 });
|
Правда, если поменять тип константы слева на double
, в Clang 19. предупреждение исчезнет. Но компилироваться оно не перестанет.
Никогда, если только у вас не C++98, не определяйте неявный operator bool
! Он всегда должен быть explicit
. Если вы боитесь, что это заставит вас делать static_cast<bool>
там, где этого не хочется делать, то не переживайте!
С++ определяет несколько контекстов, в которых explicit operator bool
все равно может быть вызван неявно: в условиях if
, for
и while
, а также в логических операциях. Этого достаточно для большинства использнований operator bool
.
Если у вас C++98... Я вам очень соболезную. Но и даже в вашем печальном случае есть решение. Чудовищно громоздкое, но решение — можете ознакомиться с устаревшей Safe Bool Idiom в свободное время в качестве домашнего задания. Если коротко, вместо operator bool
предгалалось определить
// Указатель на метод в приватном классе!
typedef void (SomePrivateClass::*bool_type) () const;
operator bool_type(); // неявное приведение к этому указателю
И тогда бы ваш объект в условных операциях неявно приводился бы к указателю, а указатель бы далее неявно приводился к bool
.