Добавление неявных преобразований c в перечисления с ограниченным пространством с глобальными операторами, особенно для побитовых флагов - плохая идея? - PullRequest
0 голосов
/ 26 марта 2020

Отсутствие неявного преобразования может быть болезненным при использовании перечислений с областью видимости для побитовых флагов:

enum class Flags: uint32_t { Foo = 1, Bar = 2 };

uint32_t foobar = Flags::Foo | Flags::Bar; // Error

Я хотел бы разрешить некоторое неявное преобразование с глобальными операторами, такими как:

template< typename T, typename R = typename std::underlying_type< T >::type, typename = typename std::enable_if< std::is_enum< T >::value && std::is_unsigned< R >::value >::type >
inline R operator |( T v1, T v2 )
{
    return static_cast< R >( v1 ) | static_cast< R >( v2 );
}

template< typename T, typename R = typename std::underlying_type< T >::type, typename = typename std::enable_if< std::is_enum< T >::value && std::is_integral< R >::value >::type >
inline bool operator ==( R u, T v )
{
    return u == static_cast< R >( v );
}

В какой ситуации эти операторы могут вызвать проблему или ошибку?

В часто задаваемых вопросах по C ++ мы можем прочитать о ограниченных и строго типизированных перечислениях:

Обычные перечисления неявно преобразуются в int, вызывая ошибки, когда кто-то не хочет, чтобы перечисление действовало как целое число.

В следующем примере:

enum class Color { red, blue };

int i = Color::blue; // error: not Color->int conversion

I Я изо всех сил пытаюсь найти сценарий ошибки с таким неявным преобразованием в базовый тип, если enum является целым числом.

Необходимость static_cast может ИМХО также быть опасной, поскольку она может отключить усечение:

enum class Color: uint64_t { red, blue = std::numeric_limits< uint64_t >::max() };

uint32_t i = static_cast< uint32_t >( Color::blue ); // No error, but invalid value

Конечно, вместо этого мы должны привести к нужному базовому типу:

uint32_t i = static_cast< std::underlying_type< Color >::type >( Color::blue ); // OK, error here

Но это так ужасно, я сомневаюсь, что смогу убедить кого-либо в моей команде используйте это ...

Редактировать

Основываясь на (многих) комментариях, вот некоторые детали.

Я не предлагаю неявное преобразование в C ++.
Я спрашиваю о потенциальных проблемах введения некоторых глобальных операторов, чтобы помочь с этим в существующей кодовой базе.

Я хотел бы, чтобы все в моей команде использовали перечисления с ограниченным диапазоном - но трудно убедить их, если им нужно используйте static_casts каждый раз, когда у них есть побитовые флаги, поэтому я пытаюсь разрешить некоторые неявные операции.

1 Ответ

0 голосов
/ 26 марта 2020

Учтите это:

enum State { ON, OFF }; // Old style enum

class Light
{
public:
    void turnOn(bool on)
    {
        cout << boolalpha << "On is " << on;
    }
};

void setLightState(Light& light, State state)
{
    light.turnOn(state); // State accidentally converted to in here
}

int main()
{
    Light l;

    setLightState(l, ON); // Outputs 'On is false'
}

В отмеченной строке state неявно преобразуется в int, который приемлем для передачи в bool. Но логика c инвертирована, поскольку ON имеет значение int 0, а OFF равно 1. enum class выдаст ошибку во время компиляции.

...