Унарным + узнать underlying от enum
От: Sm0ke Россия ksi
Дата: 04.07.23 16:38
Оценка: 8 (2)
Александреску предложил перегрузить унарный префиксный оператор + для всех енумов для получения их underlying value

https://twitter.com/incomputable/status/1676216330402381826?s=20

#include <cassert>
#include <type_traits>

template <typename Enum>
auto operator+(Enum e) -> std::enable_if_t<std::is_enum_v<Enum>, std::underlying_type_t<Enum>>
{
    return static_cast<std::underlying_type_t<Enum>>(e);
}

enum class MyEnum : unsigned int { Value1 = 1, Value2 = 2, Value3 = 3 };

int main() {
    auto e = MyEnum::Value2;
    auto val = +e;
    static_assert(std::is_same_v<decltype(val), unsigned int>);
    assert(val == 2);
}


я Заменил enable_if на концепт:

#include <cassert>
#include <type_traits>

template <typename T>
concept enum_type = std::is_enum_v<T>;

template <enum_type Enum>
constexpr auto operator + (Enum e) -> std::underlying_type_t<Enum> {
    return static_cast< std::underlying_type_t<Enum> >(e);
}

enum class MyEnum : unsigned int { Value1 = 1, Value2 = 2, Value3 = 3 };

int main() {
    auto e = MyEnum::Value2;
    auto val = +e;
    static_assert(std::is_same_v<decltype(val), unsigned int>);
    assert(val == 2);
}


Что думаете? Писать меньше текста ведь удобнее ...
Отредактировано 04.07.2023 16:40 Sm0ke . Предыдущая версия .
Re: Унарным + узнать underlying от enum
От: koenjihyakkei Россия  
Дата: 04.07.23 17:39
Оценка:
Здравствуйте, Sm0ke, Вы писали:

Идея нравится, только звездочка, мне кажется, органичнее бы смотрелась:

int main() {
    auto e = MyEnum::Value2;
    auto val = *e;
}
Re: Унарным + узнать underlying от enum
От: sergii.p  
Дата: 04.07.23 18:02
Оценка: +2
Здравствуйте, Sm0ke, Вы писали:

S>Что думаете?


не нравится. Что это делает? Меняет тип. Значит Александреску хочет нажухать систему типов малозаметным плюсом. Идея так себе.

enum class PersonId {};
enum class ManagerId {};

std::optional<Manager> find(ManagerId id) {...}

...

const auto id = PersonId{42}; // допустим прочитали из базы
const auto manager = find(ManagerId{+id}); // ну ошибка, мягко говоря, в глаза не бросается
const auto manager = find(ManagerId{std::to_underlying(id)}); // здесь мне кажется легче увидеть, что кто-то явно спятил
Re[2]: Унарным + узнать underlying от enum
От: koenjihyakkei Россия  
Дата: 04.07.23 18:37
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>не нравится. Что это делает?


Ну, например, сразу доступны все арифметические операции для енамов:
enum class MyEnum : unsigned int { Value1 = 1, Value2 = 2, Value3 = 3 };

auto e1 = MyEnum::Value2;
auto e2 = MyEnum::Value2;

auto val = *e1 + *e2;
Re[3]: Унарным + узнать underlying от enum
От: sergii.p  
Дата: 04.07.23 18:52
Оценка: +2
Здравствуйте, koenjihyakkei, Вы писали:

K>
K>auto val = *e1 + *e2;
K>


ну и val получился типа int а должен был быть MyEnum. К тому же это переопределение может быть некорректно

enum class Meters {}
enum class Kilometers {}

auto operator+(Meters m, Kilometers k) { return Meters { m + k * 1000}; }


enum class был введён для усиления системы типов. А введения скрытого преобразования типа — это шаг назад. Если не нужна строгость, можно использовать обычный enum.
Re: Унарным + узнать underlying от enum
От: serg_joker Украина  
Дата: 04.07.23 21:04
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Александреску предложил перегрузить унарный префиксный оператор + для всех енумов для получения их underlying value


Я тоже делаю перегрузку унарного плюса, но перегружаю только для is_scoped_enum (который С++23, но легко реализуем через is_enum<T> + !convertible_to<T,underlying_type<T>>).
Для non-scoped enum унарный плюс и так работает за счёт интегрального продвижения.
Разница в типе: встроенный унарный плюс даёт int, тогда как самописный может давать честный underlying type.
С одной стороны, хотелось бы иметь одинаковое поведение для scoped и non-scoped enums (а именно underlying_type), с другой — не хочется повлиять на существующий код (например, foo(+non_coped_enum_val) вызовет разные перегрузки в зависимости от того, видно ли в точке вызова определение operator+(T) -> underlying_type<T>).
Я понимаю, что вероятность нарваться на связанные с этим проблемы невелика, но и больших преимуществ от приведения non-scoped enum именно к underlying, а не int тоже нет, а там, где есть, лучше явно позвать to_underlying. Так что я выбираю безопасность (в плане невлияния на существующий код).
Отредактировано 04.07.2023 21:19 serg_joker . Предыдущая версия .
Re[2]: Унарным + узнать underlying от enum
От: serg_joker Украина  
Дата: 04.07.23 21:17
Оценка:
Здравствуйте, koenjihyakkei, Вы писали:
K>Идея нравится, только звездочка, мне кажется, органичнее бы смотрелась:

На мой взгляд, в С++ префиксная звёздочка семантически крепко ассоциируется с получением значения, на которое есть некий указатель или другой косвенный "хранитель" (типа optional). использование его для целей получения underlying type мне видится сомнительной идеей.

Опять же, скажем, если есть
enum EN{ A, B, C };

optional<EN> o;

то код

auto x = +o.value();
auto x = +*o;


смотрится лучше/понятнее (на мой вкус), чем

auto x = *o.value();
auto x = **o;


Кроме того, префисный плюс можно применять в шаблонном коде (не обязательно значит, что нужно), так что он будет равно успешно работать как со встроенными интегральными типами, так и с перечислениями.
Отредактировано 04.07.2023 21:18 serg_joker . Предыдущая версия .
Re[3]: Унарным + узнать underlying от enum
От: CreatorCray  
Дата: 04.07.23 21:56
Оценка: +3
Здравствуйте, koenjihyakkei, Вы писали:

K>Ну, например, сразу доступны все арифметические операции для енамов:

А зачем?
В тех редких случаях когда такое надо над enums проводить лучше добавить конкретную операцию для конкретного enum, а не лепить один костыль для всех.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[2]: Унарным + узнать underlying от enum
От: B0FEE664  
Дата: 05.07.23 07:54
Оценка: +1
Здравствуйте, koenjihyakkei, Вы писали:

K>Идея нравится, только звездочка, мне кажется, органичнее бы смотрелась:


уже обсуждали
И каждый день — без права на ошибку...
Re: Унарным + узнать underlying от enum
От: so5team https://stiffstream.com
Дата: 05.07.23 08:40
Оценка: +1
Здравствуйте, Sm0ke, Вы писали:

S>Александреску предложил перегрузить унарный префиксный оператор + для всех енумов для получения их underlying value


S>




Александреску покусал сам себя?

PS. Очень бы не хотелось сопровождать код с такими операторами +.
Re[3]: Унарным + узнать underlying от enum
От: SaZ  
Дата: 05.07.23 12:06
Оценка: +1
Здравствуйте, serg_joker, Вы писали:

_>Здравствуйте, koenjihyakkei, Вы писали:

K>>Идея нравится, только звездочка, мне кажется, органичнее бы смотрелась:

_>На мой взгляд, в С++ префиксная звёздочка семантически крепко ассоциируется с получением значения, на которое есть некий указатель или другой косвенный "хранитель" (типа optional). использование его для целей получения underlying type мне видится сомнительной идеей.


_>Опять же, скажем, если есть

_>
_>enum EN{ A, B, C };

_>optional<EN> o;
_>

_>то код

_>
_>auto x = +o.value();
_>auto x = +*o;
_>


_>смотрится лучше/понятнее (на мой вкус), чем


_>
_>auto x = *o.value();
_>auto x = **o;
_>


_>Кроме того, префисный плюс можно применять в шаблонном коде (не обязательно значит, что нужно), так что он будет равно успешно работать как со встроенными интегральными типами, так и с перечислениями.


Напомнило: http://rsdn.org/forum/humour/3686634.1
Автор: Arsenicum
Дата: 29.01.10
Re[3]: Унарным + узнать underlying от enum
От: B0FEE664  
Дата: 05.07.23 12:42
Оценка: +2
Здравствуйте, koenjihyakkei, Вы писали:

SP>>не нравится. Что это делает?

K>Ну, например, сразу доступны все арифметические операции для енамов:

Вот как раз такого следует избегать. Если над enum class совершаются арифметические операции, значит мы имеем дело с неверно выбранным типом. enum class имеет смысл преобразовывать в значение только если значение этого перечисления надо передать через какой-то интерфейс не умеющий работать с произвольными типами. Более того, если в enum class задаётся числовое значение для какого либо элемента, то этот код уже подозрителен.
И каждый день — без права на ошибку...
Re[4]: Унарным + узнать underlying от enum
От: Sm0ke Россия ksi
Дата: 05.07.23 16:20
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Здравствуйте, koenjihyakkei, Вы писали:


SP>>>не нравится. Что это делает?

K>>Ну, например, сразу доступны все арифметические операции для енамов:

BFE>Вот как раз такого следует избегать. Если над enum class совершаются арифметические операции, значит мы имеем дело с неверно выбранным типом. enum class имеет смысл преобразовывать в значение только если значение этого перечисления надо передать через какой-то интерфейс не умеющий работать с произвольными типами. Более того, если в enum class задаётся числовое значение для какого либо элемента, то этот код уже подозрителен.


Вроде да, а вроде и нет. Как быть с битовыми масками? Ввели бы в язык флаговый тип, то я бы с вами скорее всего согласился.
Re[5]: Унарным + узнать underlying от enum
От: B0FEE664  
Дата: 05.07.23 17:13
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Вроде да, а вроде и нет. Как быть с битовыми масками? Ввели бы в язык флаговый тип, то я бы с вами скорее всего согласился.


Для битовых масок я бы писал отдельный класс с константами. Но если хочется enum, то зачем именно enum class? Можно просто enum:

namespace enum_keys
{
enum Flags : std::uint32_t
{
  ctrl  = 0b001,
  alt   = 0b010,
  shift = 0b100
};
}// namespace enum_keys

using KbdFlags = enum_keys::Flags;

...

std::uint32_t  x = 1;
if ( KbdFlags::ctrl & x)
  ...
И каждый день — без права на ошибку...
Re[5]: Унарным + узнать underlying от enum
От: CreatorCray  
Дата: 05.07.23 19:38
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Вроде да, а вроде и нет. Как быть с битовыми масками?

Добавь constexpr ... operator | (...) для своего типа с битовыми масками
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re: Унарным + узнать underlying от enum
От: rg45 СССР  
Дата: 06.07.23 20:11
Оценка: +1
Здравствуйте, Sm0ke, Вы писали:

S>
S>template <enum_type Enum>
S>constexpr auto operator + (Enum e) -> std::underlying_type_t<Enum> {
S>    return static_cast< std::underlying_type_t<Enum> >(e);
S>}
S>


S>Что думаете?


Я думаю, здесь можно бы избавиться от дублирования и сделать запись более компактной:

template <enum_type Enum>
constexpr auto operator + (Enum e) { return std::underlying_type_t<Enum>(e); }


P.S. И да, концепты, конечно, лучше, чем SFINAE, как по мне.
--
Отредактировано 06.07.2023 20:16 rg45 . Предыдущая версия .
Re[2]: Унарным + узнать underlying от enum
От: rg45 СССР  
Дата: 06.07.23 20:13
Оценка: +1
Здравствуйте, so5team, Вы писали:

S>PS. Очень бы не хотелось сопровождать код с такими операторами +.


Я бы тоже предпочел функцию с понятным именем, чем загадочный оператор.
--
Re[3]: Унарным + узнать underlying от enum
От: PM  
Дата: 07.07.23 13:20
Оценка: 8 (2) +3
Здравствуйте, rg45, Вы писали:

R>Я бы тоже предпочел функцию с понятным именем, чем загадочный оператор.


В C++23 уже есть `std::to_underlying()`
https://en.cppreference.com/w/cpp/utility/to_underlying

Ну или `fmt::underlying()` из fmtlib, если лень свою писать.
https://fmt.dev/dev/api.html#_CPPv4I0EN3fmt10underlyingE12underlying_tI4EnumE4Enum
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.