операторы enum не работают - PullRequest
0 голосов
/ 29 июня 2018

Мое приложение имеет множество перечислений битовых полей, которым требуются различные вспомогательные функции (toString (), fromString (), countSetBits (), isValid () и т. Д.). Поэтому я использовал CRTP для создания базового класса со статическими функциями для всего, что мне нужно. Все отлично работает, КРОМЕ, я не могу заставить работать побитовые операторы. Итак ...

Почему операторы 1 и 2 не позволяют компилировать A и B?

Операторы 3 и 4 работают, и они выглядят одинаково для меня. (На самом деле, реализация 4 не компилируется, и я мог бы также помочь с этим ...) (броски в стиле C используются только для экономии места)

Тупик. Пожалуйста, помогите!

#include <type_traits>

using EnumUnderlying_t = unsigned;

template <typename EnumWrapper_t> struct EnumBitfieldBase {
    //CRTP allows for lots of handy static functions eliminating code duplication.  For example:
    static void test(){
        if constexpr (!std::is_same<EnumUnderlying_t, typename std::underlying_type<typename EnumWrapper_t::Enum>::type>::value){
        throw;
    }
}
//also: toString(), fromString(), countSetBits(), isValid(), largestValidValue(), allBitsSet() etc...
};

//operator #1
template <typename EnumWrapper_t>
inline constexpr typename EnumWrapper_t::Enum operator|(const typename EnumWrapper_t::Enum L, const typename EnumWrapper_t::Enum R) {
    return (typename EnumWrapper_t::Enum)((EnumUnderlying_t)L | (EnumUnderlying_t)R);
}

//operator #2
template <typename EnumWrapper_t>
inline constexpr typename EnumWrapper_t::Enum & operator|=(typename EnumWrapper_t::Enum & l, const typename EnumWrapper_t::Enum R) {
    return (typename EnumWrapper_t::Enum &)((EnumUnderlying_t &)l |= (EnumUnderlying_t)R);
}

struct Option : public EnumBitfieldBase<Option> {
    enum Enum : EnumUnderlying_t {
        None = 0,
        Lame = 1 << 0,
        Boring = 1 << 1,
        Stupid = 1 << 2
    };
};

/*
//operator #3
inline constexpr Option::Enum operator|(const Option::Enum L, const Option::Enum R) {
    return (Option::Enum)((EnumUnderlying_t)L | (EnumUnderlying_t)R);
}

//operator #4
inline constexpr Option::Enum & operator|=(Option::Enum & l, const Option::Enum R) {
        return (Option::Enum &)((EnumUnderlying_t &)l | (EnumUnderlying_t)R);
}
*/

int main(void) {
    Option::test();
    Option::Enum options{ Option::Lame | Option::Boring };//A -works only with c++17...why?
    options = (Option::Stupid | Option::Boring);//B -only works with operator #3
    options |= Option::Lame;//C -only works with operator #4
    if (options & Option::Lame) { /*do something lame*/ }
    return 0;
}

ОБНОВЛЕНИЕ: Хорошо, я выяснил, что операторы 1 и 2 не будут работать из-за "не выведенного контекста". По сути, вывод шаблона не будет работать для чего-то слева от :: ... Так каков правильный путь к базовым классам (или иным образом решить проблему) перечислений, используемых в качестве битовых полей? Я не хочу использовать трюк с пространством имен, потому что мне нужен тип для участия в выводе шаблона. Кто-нибудь?

Ответы [ 2 ]

0 голосов
/ 30 июня 2018

Это лучшее решение, которое я нашел, чтобы заставить операторов 1 и 2 работать. Пожалуйста, напишите, если вы знаете лучший способ!

//operator #1
template <typename T, typename = std::enable_if<std::is_enum<T>::value>>
inline constexpr T operator|(const T L, const T R)
{
    return static_cast<T>(static_cast<EnumUnderlying_t>(L) | static_cast<EnumUnderlying_t>(R));
}

//operator #2
template <typename T, typename = std::enable_if<std::is_enum<T>::value>>
inline constexpr T & operator|=(T & l, const T R)
{
    l = static_cast<T>(static_cast<EnumUnderlying_t>(l) | static_cast<EnumUnderlying_t>(R));
    return l;
}
0 голосов
/ 29 июня 2018

Вы должны объявить их как const методы; в противном случае им будет разрешено работать только с l-значениями, а ваши константы не являются l-значениями (они являются const, obvioulsy).

//operator #1
template <typename EnumWrapper_t>
inline constexpr typename EnumWrapper_t::Enum operator|(const typename EnumWrapper_t::Enum L, const typename EnumWrapper_t::Enum R) const {
    return (typename EnumWrapper_t::Enum)((EnumUnderlying_t)L | (EnumUnderlying_t)R);

(Изменение в последнем слове в третьей строке)

...