Допустим, я сумасшедший и решил создать следующее чудовище:
#include <type_traits>
#include <iostream>
// Utility proxy type - convertible back to E but also permits bool conversion
// for use in conditions.
//
// e.g.
// Foo f = Foo::Bar & Foo::Baz;
// if (f & Foo::Baz) { /* ... */ }
//
template <typename E, typename = std::enable_if_t<std::is_enum_v<E>, void>>
struct EnumToBoolProxy
{
operator E() const
{
return _val;
}
explicit operator bool()
{
using UT = std::underlying_type_t<E>;
return static_cast<UT>(_val) != 0;
}
private:
const E _val;
EnumToBoolProxy(const E val) : _val(val) {}
friend EnumToBoolProxy operator&(const E, const E);
friend EnumToBoolProxy operator|(const E, const E);
};
enum class Foo
{
Bar = 1, Baz = 2, Boi = 4
};
EnumToBoolProxy<Foo> operator&(const Foo lhs, const Foo rhs)
{
using UT = std::underlying_type_t<Foo>;
return static_cast<Foo>(static_cast<UT>(lhs) & static_cast<UT>(rhs));
}
EnumToBoolProxy<Foo> operator|(const Foo lhs, const Foo rhs)
{
using UT = std::underlying_type_t<Foo>;
return static_cast<Foo>(static_cast<UT>(lhs) | static_cast<UT>(rhs));
}
int main()
{
// Good
if ((Foo::Bar | Foo::Baz) & Foo::Baz)
std::cout << "Yay\n";
// Fine
const bool isFlagSet((Foo::Bar | Foo::Baz) & Foo::Baz);
std::cout << isFlagSet << '\n';
// Meh
auto proxyThing = (Foo::Bar | Foo::Baz) & Foo::Baz;
}
Цель состоит в том, чтобы:
- Иметь перечисление с областью видимости таким образом, чтобы значения были
Foo::x
и имеют тип Foo
(симметрия!) - Уметь выполнять некоторую "побитовую" арифметику с ними и получать
Foo
обратно - Уметь проверять результат наноль
- Но не позволяйте людям обычно использовать перечисление как арифметический тип
Ради забавы я стараюсь избегать:
- Использованиестандартное перечисление
- Делать это вместо этого со свободными функциями
IsFlagSet
На секунду игнорировать несоответствие невозможности выполнить проверку на ноль без предварительного &
- или |
-операция…
Кажется позором, что мои пользователи все еще могут «получить» EnumToBoolProxy
(то есть proxyThing
).Но, поскольку невозможно добавить каких-либо участников в Foo
, и поскольку operator bool
должен быть участником, я не могу найти какой-либо другой способ сделать это.
Конечно, это не такреальная проблема, так как они не могут многое сделать с EnumToBoolProxy
.Но это все еще похоже на утечку абстракции, поэтому мне любопытно: правильно ли я говорю, что это просто невозможно по своей сути?Что нет способа «выбрать и выбрать» непрозрачность enoped-enum, как это?Или есть какой-то способ скрыть этот тип прокси, все еще используя его как средство преобразования в bool для проверки «результата» операций &
/ |
?Как бы вы это сделали?