Как включить enum class в качестве аргументов шаблона класса? - PullRequest
0 голосов
/ 14 октября 2019

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

После нескольких повторений в функциях, подобных этим ...

enum class HBeams
{
    HEB100,
    enumSize
};

void HBeam::CreatePredefinedHBeam(std::map<HBeams, HBeam *> & _library, HBeams _key, ushort _width, ushort _height)
{
    if(_library.count(_key) == 0)
    {
        _library.insert(std::pair<HBeams, HBeam *>(_key, new HBeam{ _width, _height } ));
        if(_library.count(_key) == 1)
        {
            Logger::Log(Status::OK, "Created predefined HBeam with ID ", static_cast<int>(_key));
            return;
        }
    }

    Logger::Log(Status::Warning, "Failed to create predefined HBeam with ID ", static_cast<int>(_key));
    return;
}

... Я чувствовал, что должен создать для этого универсальный контейнерный класс. Однако

// Library.h
template <class T, enum class E>
class Library
{
    std::map<E, T*>  m_entries;

public:
    void  Insert(T* _entry, E _enum);
};

// Library.cpp
template<class T, enum class E>
void Library<T, E>::Insert(T* _entry, E _enum)
{
  ...
}

... сначала говорит мне, что я должен использовать 'enum', а не 'enum class' , затем мой аргумент шаблона для параметра шаблона нетипичного типа долженбыть выражением . Кажется, я могу преодолеть это, определив все перечисляемые классы в заголовке класса Library следующим образом:

enum class HBeams
{
    HEB100,
    enumSize
};

template <class T>
class HBeamLibrary
{
    std::map<HBeams, T*>  m_entries;

public:
    void  Insert(T* _entry, HBeams _key);
};

... но тогда мне все равно придется вручную создавать класс для каждой реализации. Есть ли способ заставить работать "шаблон класса enum"?

Ответы [ 2 ]

0 голосов
/ 14 октября 2019

Вы можете выполнить простое условие для второго аргумента шаблона:

template <class T, class E, class = typename std::enable_if<std::is_enum<E>::value>::type>
class Library
{
    std::map<E, T*>  m_entries;

public:
    void Insert(T* _entry, E _enum) {
        if(m_entries.count(_enum) == 0)
        {
            m_entries.insert(std::pair<E, T*>(_enum, _entry));
            if(m_entries.count(_enum) == 1)
            {
                return;
            }
        }
    }
};

Объяснение:

E - может быть как class, так и enum.
ТретийПараметр класса - Только допустимый, если e является enum.

Пример:

enum class A {
    A,
    B
};
class Test {};
Library<Test, A> l; // The code will compile
Test t;
l.Insert(&t, A::A); // Works just fine

// ----------------

Library<Test, Test> l; // The code won't compile
// Error: "template argument 3 is invalid"

Как это работает?

enable_if объявление выглядит следующим образом:

/// Define a member typedef @c type only if a boolean constant is true.
template<bool, typename _Tp = void>
struct enable_if { }; // no type named `type` is declared as class sub type

// Partial specialization for true.
template<typename _Tp>
struct enable_if<true, _Tp> { // if the condition is true
    typedef _Tp type; // type named `type` is declared as class sub type
};

enable_if дает вам возможность проверить условие во время компиляции. Если условие установлено на true, то будет вызвана частичная специализация для true, которая содержит подтип с именем type. В этом случае третий аргумент шаблона получит допустимый и существующий тип, и все будет работать нормально.

Интересный случай, когда для условия enable_if установлено значение false. В этом случае эта структура не будет содержать подтип с именем type, и любая попытка доступа к этому члену типа, например:

std::enable_if<false::value>::type

вызовет ошибку компиляции.

0 голосов
/ 14 октября 2019

Ваш library класс должен быть примерно таким:

// Library.h
template <class T, class E>
class Library
{
    static_assert(std::is_enum<E>::value);

    std::map<E, T*>  m_entries;
public:
    void  Insert(T* entry, E enum)
    {
        // ...
    }
};

с использованием как:

enum class HBeams {/*..*/};
class HBeam;

Library<HBeam, HBeams> library;
...