Добавить специализацию методов для всех классов перечисления в C++.

У меня есть следующие методы (вообще говоря) в моем классе битового потока:

class BitStream
{
    void StoreBits( unsigned int data, unsigned int numBits );
    
    public:
    template<typename T>
    void WriteType( T value ) { StoreBits( value, sizeof(T) * 8 ) );

    template<>
    void WriteType( float value ) { StoreBits( *(unsigned int*)&value, sizeof(float) * 8 ) );
};

Кто-то бросил класс перечисления строгого типа в WriteType, и, очевидно, перечисление не преобразуется автоматически в целое число без знака.

Что я могу сделать, чтобы написать специализацию, которая обрабатывает все случаи перечисления, используя одно и то же имя функции?

Это упрощение проблемы — у меня есть экземпляры класса enum, которые выбрасываются в ряд других методов-членов BitStream, которые мне приходится обрабатывать, таких как ReadType, StreamType (чтение или запись), SmallestRangeWrite‹Min,Max›() и т. д.

У меня есть поддержка С++ 17. Я знаю, что есть std::is_enum, но как я могу специализироваться на этом здесь, особенно во время компиляции?

Можно как-нибудь написать:

template<typename T>inline void BitStream::WriteType( T value ) 
{
  if (type_is_enum())
    StoreEnum<T>(value)
  else
    StoreBits( value, sizeof(T) * 8 ) );
}

Мне нужно убедиться, что метод StoreBits не компилируется, когда T является перечислением. Просто избежать этого во время выполнения с веткой недостаточно.


person Steven    schedule 02.10.2020    source источник
comment
Кажется, ты знаешь is_enum. Просто сделайте type_is_enum() --› is_enum_v<T> лайк этому.   -  person cigien    schedule 02.10.2020
comment
Не забудьте if constexpr   -  person Dmitry Kuzminov    schedule 02.10.2020
comment
Пожалуйста, сделайте минимально воспроизводимый пример Что такое u32? Компилятор кода делает что-то неправильно для enum типов?   -  person cigien    schedule 02.10.2020
comment
@cigien Где находится 'is_enum_v‹T›' - я вижу is_enum‹T›::value; даже в этом случае, как я могу убедиться, что два разных пути кода скомпилированы, или это просто работает?   -  person Steven    schedule 02.10.2020
comment
В C++17 is_enum_v<T> является псевдонимом для is_enum<T>::value.   -  person parktomatomi    schedule 02.10.2020


Ответы (2)


Проверьте, является ли тип перечислением и, не преобразуется ли он в u32. Это позволяет различать обычные enum и enum class. Если это перечисление, но его нельзя преобразовать в u32, то это enum class:

template<typename T>
inline void BitStream::WriteType(T value) 
{
    if constexpr (std::is_enum_v<T> && !std::is_convertible_v<T, u32>)
        // enum class
        StoreEnum<T>(value)
    else
       // normal enum or other type that is convertible to u32
        StoreBits(value, sizeof(T) * 8 ));
}

Поскольку ваши шаблоны функций StoreEnum и StoreBits не могут принимать оба, вам нужно использовать здесь if constexpr, чтобы избежать проверки типа при компиляции.

person Nikos C.    schedule 02.10.2020
comment
Это кажется разумным, но, возможно, мой компилятор на самом деле не C++17, потому что у меня есть is_enum, но не is_enum_v и не is_convertible_v. Это сокращение для is_enum‹T›::value ? - person Steven; 02.10.2020
comment
@ Стивен Да, это просто сокращение. Какой компилятор вы используете? С GCC и Clang вам нужно использовать -std=c++17 (или -std=gnu++17, чтобы также получить расширения GCC) при компиляции. - person Nikos C.; 02.10.2020
comment
Итак, у меня возникла проблема, когда «StoreBits» все еще пытается скомпилироваться, хотя тип является перечислением; Я знаю, что во время выполнения будет выбрана правильная ветвь, но есть ли способ сделать это при компиляции, чтобы метод StoreBits даже не рассматривался для компиляции, если T является перечислением? Я полагаю, его можно просто использовать как условие аргумента шаблона? - person Steven; 02.10.2020
comment
@Стивен Тогда тебе нужно if constexpr. Вся эта информация должна быть в самом вопросе. - person cigien; 02.10.2020
comment
@Стивен Да. Обновил ответ. - person Nikos C.; 02.10.2020
comment
@cigien Я обновил вопрос, извините за неясность. - person Steven; 02.10.2020

Лучшей функцией C++17 (это мое личное мнение) является constexpr if. Это означает, что вам не нужно определять явные специализации. Это сработает:

template<typename T>inline void BitStream::WriteType(T value) 
{
    if constexpr (std::is_enum_v<T>)
        StoreEnum<T>(value);
    else
        StoreBits(value, sizeof(T) * 8);
}

Использование constexpr if важно, так как вы рискуете ошибкой компиляции, если StoreEnum или StoreBits не определены для типа T.

person Dmitry Kuzminov    schedule 02.10.2020
comment
@НикосС. Что произойдет, если T имеет тип double? Будет ли StoreEnum<T>(T) определен? - person Dmitry Kuzminov; 02.10.2020
comment
Так что это заменяет мне необходимость помещать его в StoreHelper‹std::is_enum‹T›::value›::Store(T), где я специализирую StoreHelper на значения true и false, чтобы убедиться, что для него скомпилирован только правильный код. тип Т, верно? - person Steven; 02.10.2020
comment
@Стивен, если я правильно тебя понял, ответ - да. - person Dmitry Kuzminov; 02.10.2020