Как мне определить / специализироваться на типографский тип в классе?

StackOverflow https://stackoverflow.com//questions/21046352

  •  21-12-2019
  •  | 
  •  

Вопрос

У меня есть следующая ситуация: Моя проблема вращается вокруг, используя сильно напечатанные классы Enum в качестве флагов (так же, как в C # с атрибутом флага). Я знаю, что это не так, как должны использоваться классы Enum, которые должны были использоваться в первую очередь, но это не точка этого вопроса.

Я определил несколько операторов и функций для использования на этих классах Enum, а также таможенная черта для различения нормальных перечислений от флага. Вот пример:

// Default type_trait which disables the following operators
template <typename T> struct is_flags : std::false_type {};

// Example operator to use enum class as flags
template <typename T>
std::enable_if_t<std::is_enum<T>::value && is_flags<T>::value, T&>
operator|=(T &t1, const T t2)
{
    return t1 = static_cast<T>(static_cast<std::underlying_type_t<T>>(t1) | 
                               static_cast<std::underlying_type_t<T>>(t2));
};
.

Теперь, если я определим любой генеракодицетагкод, я могу сделать следующее:

enum class Foo { A = 1, B = 2 };

enum class Bar { A = 1, B = 2 };

// Declare "Bar" to be useable like Flags
template <> struct is_flags<Bar> : std::true_type {}; 

void test()
{
    Foo f;
    Bar b;
    f |= Foo::A; // Doesn't compile, no operator |=
    b |= Bar::A; // Compiles, type_trait enables the operator
}
.

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

Однако, когда enum class не определен в области пространства имен, я столкнулся с проблемой:

struct X
{
    enum class Bar { A = 1, B = 2 };

    // Following line gives: C3412: Cannot specialize template in current scope
    template <> struct is_flags<Bar> : std::true_type {};
}
.

Тип черты не может быть специализирован здесь. Мне нужно будет определить черта вне X, что возможно, но разделяет «атрибут флаг» из объявления Enum. Было бы так приятно использовать это в нашем коде, так как флаги используются по всему месту, но довольно старомодно (enum class + int). Все решения этой проблемы я нашел до сих пор сосредоточиться на классах вместо Enums, где решение намного проще, поскольку я могу определить черта в качестве члена самого класса. Однако enums не могут наследовать, содержат Typedefs или все, что может потребоваться, чтобы дифференцировать определенный класс Enum от другого.

Так что есть возможность определить какую-то черта в классном объеме, которые можно использовать в глобальном пространстве пространства имен для распознавания специальных типов классов Enum?

<Сильные> Редактировать: Я должен добавить, что я использую Visual Studio 2013.

Обновление: Спасибо за ответы, метка-решение работала действительно хорошо, хотя мне пришлось сделать тонкое изменение (что делает его еще более простым в процессе). Теперь я использую эту черту пользовательского типа:

template <typename T>
struct is_flags
{
private:
    template <typename U> static std::true_type check(decltype(U::Flags)*);
    template <typename> static std::false_type check(...);

    typedef decltype(check<T>(0)) result;
public:
    static const bool value = std::is_enum<T>::value && result::value;
};
.

Теперь все, что мне нужно сделать, это добавить #define в класс Enum, независимо от того, какую область это в:

enum class Foo { Flags, A = 0x0001, B = 0x0002 };
.

См. Также Здесь для аналогичной проблемы и решения.

Обновление 2: Поскольку Visual Studio 2013 Обновление 2 Это решение приведет к выбору компилятора, когда TrancodicCode Trait применяется к заголовкам базы IOS. Поэтому мы используем другой и более чистый подход, мы используем класс шаблона, который действует как хранилище для Flags и определяет все операторы на себя без какой-либо магии типа TRAIT. Класс шаблона может быть создан неявным с базовым генеракодицетагкодом и явным с базовым типом. Работает очарование и гораздо меньше генеракодицетагкод-беспорядка.

Это было полезно?

Решение

Вы можете пометить сам перечисление:

#include <type_traits>

template<typename T>
struct is_flags {
    private:
    typedef typename std::underlying_type<T>::type integral;
    template<integral> struct Wrap {};

    template<typename U>
    static constexpr std::true_type check(Wrap<integral(U::EnumFlags)>*);

    template<typename>
    static constexpr std::false_type check(...);

    typedef decltype(check<T>(0)) result;

    public:
    static constexpr bool value = std::is_enum<T>::value && result::value;
};

namespace Detail {
    template <bool>
    struct Evaluate;

    template <>
    struct Evaluate<true> {
        template <typename T>
        static T apply(T a, T b) { return T(); }
    };
}

template <typename T>
T evalueate(T a, T b)
{
    return Detail::Evaluate<is_flags<T>::value>::apply(a, b);
}

enum class E{ A = 1, B, C };
struct X {
    enum class F{ EnumFlags, A = 1, B, C };
};

int main ()
{
    // error: incomplete type ‘Detail::Evaluate<false>’ used in nested name specifier
    // evalueate(E::A, E::B);
    evalueate(X::F::A, X::F::B);
}
.

Другие советы

Вот уродливое решение с использованием ADL вместо черт (конечно, вы можете скрыть ADL внутри черты):

Новый шаблон оператора:

struct my_unique_enum_flag_type;

// Example operator to use enum class as flags
template <typename T>
enable_if_t<std::is_enum<T>::value
            && std::is_same<decltype(is_flags(std::declval<T>())),
                            my_unique_enum_flag_type>::value, T&>
operator|=(T &t1, const T t2)
{
    return t1 = static_cast<T>(static_cast<underlying_type_t<T>>(t1) | 
                               static_cast<underlying_type_t<T>>(t2));
};
.

Определение is_flags для Bar:

struct X
{
    enum class Bar { A = 1, B = 2 };

    friend my_unique_enum_flag_type is_flags(Bar);
};

int main()
{
    X::Bar a = X::Bar::A;
    a |= X::Bar::B;
}
.

(желательно, используйте более уникальное имя, чем is_flags для ADL)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top