Я пытаюсь создать безопасные флаги С++ с использованием шаблонов. Я также хочу различать флаг a и флаг s (будучи нулем, одним или несколькими флагами).
Приведенное ниже решение работает хорошо, за исключением EnumFlag<T> operator | (T, T)
, из-за которого все |
-операции над перечислениями возвращают тип EnumFlag
. Это ломает много кода. Любые трюки, чтобы исправить это? В моем коде я делаю следующее, однако жесткое кодирование Option
здесь не вариант. Как сделать это универсальным?
EnumFlag<typename std::enable_if<std::is_same<T, Option>::value, T>::type> operator | (T l, T r)
Изменение этого на...
EnumFlag<T> operator | (T l, T r)
...причины ломает все. Я хотел бы что-то вроде этого (не компилируемый код). Или любая другая лучшая идея!
EnumFlag<typename std::enable_if<std::already_expanded<EnumFlag<T>>::value, T>::type> operator | (T l, T r)
Полный компилируемый код:
EnumFlag.h
#ifndef __classwith_flags_h_
#define __classwith_flags_h_
#include <type_traits>
enum class Option
{
PrintHi = 1 << 0,
PrintYo = 1 << 1,
PrintAlot = 1 << 2
};
template <typename T>
class EnumFlag
{
public:
using UnderlayingType = typename std::underlying_type<T>::type;
EnumFlag(const T& flags)
: m_flags(static_cast<UnderlayingType>(flags))
{}
bool operator & (T r) const
{
return 0 != (m_flags & static_cast<UnderlayingType>(r));
}
static const T NoFlag = static_cast<T>(0);
private:
UnderlayingType m_flags;
};
template<typename T>
EnumFlag<typename std::enable_if<std::is_same<T, Option>::value, T>::type> operator | (T l, T r)
{
return static_cast<T>(static_cast<typename EnumFlag<T>::UnderlayingType>(l) | static_cast<typename EnumFlag<T>::UnderlayingType>(r));
}
class ClassWithFlags
{
public:
using Options = EnumFlag < Option >;
void doIt(const Options &options);
};
#endif
EnumFlag.cpp
#include "EnumFlag.h"
#include <iostream>
void ClassWithFlags::doIt(const Options &options)
{
if (options & Option::PrintHi)
{
std::cout << "Hi" << std::endl;
}
if (options & Option::PrintYo)
{
std::cout << "Yo!" << std::endl;
}
}
int main()
{
ClassWithFlags classWithFlags;
classWithFlags.doIt(Option::PrintHi | Option::PrintAlot);
}
> ДЕМО ‹
Фактический код будет содержать намного больше операторов, однако этого достаточно, чтобы проиллюстрировать проблему.
Это менее навязчивое решение (но все же слишком навязчивое)
template<typename T>
typename std::underlying_type<T>::type operator | (T l, T r)
{
return (static_cast<typename std::underlying_type<T>::type>(l) | static_cast<typename std::underlying_type<T>::type>(r));
}
Недостаточно бога, тогда EnumFlag(const std::underlying_type<T> &flags)
должно существовать, и я теряю безопасность типа. Кроме того, я хотел бы, чтобы глобальные перегрузки операторов создавались только для действительно необходимых типов. Макросы тоже не Бог, потому что я хочу разрешить объявление EnumFlag
s внутри классов. Глобальных перегрузок быть не может, поэтому мне нужны два вызова макросов в разных местах для создания на EnumFlag
.
Решение должно быть чистым C++11/stl.
EnumFlag<T>
вT
не устранит проблемы, вызванныеEnumFlag<T> operator | (T l, T r)
? - person Pradhan   schedule 06.03.2015EnumFlag
экземпляров, когда вам это не нужно, просто для обратного преобразования. Также это позволит вызыватьfoo(const Option &o)
с экземпляромOptions
. Следовательно, теряя тип safty. Внутриfoo
у нас может быть переключатель, обрабатывающий все случаи, но все равно ничего не произойдет... - person Mathias   schedule 06.03.2015std::bitset
подержать ваши флаги? - person Neil Kirk   schedule 06.03.2015