Стандартные битмаски С++ 11 с использованием класса enum
Можете ли вы реализовать стандартную совместимость (как описано в 17.5.2.1.3 черновика n3242) безопасными битами с использованием класса enum? Как я его читал, тип T является битовой маской, если он поддерживает операторы |, &, ^, ~, | =, & = и ^ =, и, кроме того, вы можете делать, если (l & r), где l и r имеют тип T. Отсутствие в списке оператора!= и == и разрешить сортировку, вероятно, также хочет перегрузить <.
Получение операторов для работы - это просто раздражающий шаблонный код, но я не вижу, как это сделать, если (l & r). По крайней мере, следующее не компилируется с GCC (кроме того, что это чрезвычайно опасно, поскольку позволяет ошибочное неявное преобразование в int):
enum class Foo{
operator bool(){
return (unsigned)*this;
}
};
EDIT: теперь я точно знаю, что классы перечисления не могут иметь членов. Фактический вопрос, как это сделать, если (l & r) остается, хотя.
Ответы
Ответ 1
Я думаю, вы можете...
Вы должны добавить операторов для битмаски. Я не делал этого здесь, но вы могли бы легко перегрузить любой реляционный оператор.
/**
*
*/
// NOTE: I changed to a more descriptive and consistent name
// This needs to be a real bitmask type.
enum class file_permissions : int
{
no_perms = 0,
owner_read = 0400,
owner_write = 0200,
owner_exe = 0100,
owner_all = 0700,
group_read = 040,
group_write = 020,
group_exe = 010,
group_all = 070,
others_read = 04,
others_write = 02,
others_exe = 01,
others_all = 07,
all_all = owner_all | group_all | others_all, // 0777
set_uid_on_exe = 04000,
set_gid_on_exe = 02000,
sticky_bit = 01000,
perms_mask = all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit, // 07777
perms_not_known = 0xffff,
add_perms = 0x1000,
remove_perms = 0x2000,
symlink_perms = 0x4000
};
inline constexpr file_permissions
operator&(file_permissions x, file_permissions y)
{
return static_cast<file_permissions>
(static_cast<int>(x) & static_cast<int>(y));
}
inline constexpr file_permissions
operator|(file_permissions x, file_permissions y)
{
return static_cast<file_permissions>
(static_cast<int>(x) | static_cast<int>(y));
}
inline constexpr file_permissions
operator^(file_permissions x, file_permissions y)
{
return static_cast<file_permissions>
(static_cast<int>(x) ^ static_cast<int>(y));
}
inline constexpr file_permissions
operator~(file_permissions x)
{
return static_cast<file_permissions>(~static_cast<int>(x));
}
inline file_permissions &
operator&=(file_permissions & x, file_permissions y)
{
x = x & y;
return x;
}
inline file_permissions &
operator|=(file_permissions & x, file_permissions y)
{
x = x | y;
return x;
}
inline file_permissions &
operator^=(file_permissions & x, file_permissions y)
{
x = x ^ y;
return x;
}
Ответ 2
Я не совсем уверен, каковы ваши критерии приема, но вы можете просто сделать operator &
вернуть класс-оболочку с соответствующими преобразованиями и explicit operator bool
:
#include <type_traits>
template<typename T> using Underlying = typename std::underlying_type<T>::type;
template<typename T> constexpr Underlying<T>
underlying(T t) { return Underlying<T>(t); }
template<typename T> struct TruthValue {
T t;
constexpr TruthValue(T t): t(t) { }
constexpr operator T() const { return t; }
constexpr explicit operator bool() const { return underlying(t); }
};
enum class Color { RED = 0xff0000, GREEN = 0x00ff00, BLUE = 0x0000ff };
constexpr TruthValue<Color>
operator&(Color l, Color r) { return Color(underlying(l) & underlying(r)); }
Все остальные операторы могут продолжать возвращать Color
, конечно:
constexpr Color
operator|(Color l, Color r) { return Color(underlying(l) | underlying(r)); }
constexpr Color operator~(Color c) { return Color(~underlying(c)); }
int main() {
constexpr Color YELLOW = Color::RED | Color::GREEN;
constexpr Color WHITE = Color::RED | Color::GREEN | Color::BLUE;
static_assert(YELLOW == (WHITE & ~Color::BLUE), "color subtraction");
return (YELLOW & Color::BLUE) ? 1 : 0;
}
Ответ 3
Скопированные перечисления (созданные с помощью enum class
или enum struct
) не являются классами. Они не могут иметь функции-члены, они просто предоставляют вложенные счетчики (не видны на уровне пространства имен).
Ответ 4
Отсутствуют в списке операторы!= и ==
Эти операторы уже поддерживаются типами перечисления, целыми типами и std::bitset
, поэтому нет необходимости их перегружать.
и разрешить сортировку, возможно, также требуется перегрузить <.
Почему вы хотите сортировать битмаски? Является ли (a | b) больше (a | c)? Является std::ios::in
меньше std::ios::app
? Это имеет значение? Реляционные операторы всегда определяются для типов перечисления и целых типов.
Чтобы ответить на главный вопрос, вы бы выполнили &
как перегруженную не-членную функцию:
Foo operator&(Foo l, Foo r)
{
typedef std::underlying_type<Foo>::type ut;
return static_cast<Foo>(static_cast<ut>(l) & static_cast<ut>(r));
}
Я полагаю, что все необходимые операции для типов битмаски могут быть определены для скопированных перечислений, но не для таких требований, как
Ci и Cj отличны от нуля, а Ci и Cj iszero
и
Значение Y задано в объекте X, это выражение X и Y отличное от нуля.
Поскольку скопированные перечисления не поддерживают принудительное преобразование в целые типы, вы не можете надежно проверить, отличен ли он от нуля или нет. Вам нужно написать if ((X&Y) != bitmask{})
, и я не думаю, что это намерение комитета.
(Первоначально я думал, что они могут использоваться для определения типов битмаски, а затем вспомнили, что я попытался реализовать один из них с использованием облачных перечислений и столкнулся с проблемой при тестировании на нуль/ненулевое значение.)
Изменить: я только что вспомнил, что std::launch
- это тип перечислимого типа и тип битовой маски... поэтому видимые области перечисления могут быть типами битмаски!
Ответ 5
Краткий пример ниже перечисляемых флагов.
#indlude "enum_flags.h"
ENUM_FLAGS(foo_t)
enum class foo_t
{
none = 0x00
,a = 0x01
,b = 0x02
};
ENUM_FLAGS(foo2_t)
enum class foo2_t
{
none = 0x00
,d = 0x01
,e = 0x02
};
int _tmain(int argc, _TCHAR* argv[])
{
if(flags(foo_t::a & foo_t::b)) {};
// if(flags(foo2_t::d & foo_t::b)) {}; // Type safety test - won't compile if uncomment
};
ENUM_FLAGS (T) - макрос, определенный в enum_flags.h (менее 100 строк, бесплатно для использования без ограничений).