Как включить класс 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', а затем мой аргумент шаблона для нетипового параметра шаблона должен быть выражением. Кажется, я могу преодолеть это, определив все классы перечисления в заголовке класса библиотеки следующим образом:

enum class HBeams
{
    HEB100,
    enumSize
};

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

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

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


person Paauwer    schedule 14.10.2019    source источник


Ответы (2)


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

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

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

person CoralK    schedule 14.10.2019
comment
Поскольку приведенное выше решение решило проблему для меня (как и static_assert), не могли бы вы объяснить правильный синтаксис для перемещения третьего параметра класса в отдельный файл реализации? - person Paauwer; 16.10.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;
person Jarod42    schedule 14.10.2019