Ich versuche, typsichere C++ - Flags mit Vorlagen zu erstellen. Ich möchte auch unterscheiden zwischen ein Flag und Flag s (als Null, eine oder viele Flags).Vorlage für Typ sicher C++ 11 enum Klasse Flags
Die folgende Lösung funktioniert gut, mit Ausnahme von EnumFlag<T> operator | (T, T)
, die alle |
-Operationen auf enums zum Rückgabetyp EnumFlag
verursacht. Das bricht viel Code. Irgendwelche Tricks um das zu beheben? In meinem Code mache ich folgendes, jedoch ist eine harte Codierung Option
hier keine Option. Wie mache ich das generisch?
EnumFlag<typename std::enable_if<std::is_same<T, Option>::value, T>::type> operator | (T l, T r)
ändern, dies zu ...
EnumFlag<T> operator | (T l, T r)
... von Ursache bricht alles. Ich möchte so etwas (nicht Compilabel-Code). Oder irgendeine andere bessere Idee!
EnumFlag<typename std::enable_if<std::already_expanded<EnumFlag<T>>::value, T>::type> operator | (T l, T r)
komplette übersetzbar Code:
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);
}
>DEMO <
Der eigentliche Code wird viel mehr Operatoren enthalten, aber dies ist genug, um das Problem zu veranschaulichen.
Eine weniger aufdringliche Lösung ist dies (aber immer noch zu aufdringlich)
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));
}
Gott nicht genug, dann EnumFlag(const std::underlying_type<T> &flags)
vorhanden sein muss und ich Typ Safty verlieren. Außerdem möchte ich, dass die globalen Operatorüberladungen nur für die tatsächlich benötigten Typen erstellt werden. Macros ist auch kein Gott, weil ich die Deklaration von EnumFlag
s in Klassen erlauben will. Die globalen Überladungen können nicht vorhanden sein, daher benötige ich zwei Makroaufrufe an unterschiedlichen Positionen, um sie unter EnumFlag
zu erstellen.
Die Lösung muss rein C++ 11/stl sein.
Würde von 'EnumFlag keine nicht-explizite Konvertierung Hinzufügen' auf 'T 'behebt die Probleme, die durch 'EnumFlag operator | (T l, T r) "? –
Pradhan
Ich denke, das ist ein Workaround, Erstellen von 'EnumFlag' Instanzen, wenn Sie nicht brauchen, nur um zurück zu konvertieren. Dies erlaubt auch den Aufruf von 'foo (const Option & o)' mit einer Instanz von 'Options'. Daher verliert Typ Sicherheit. Innerhalb 'foo' haben wir vielleicht einen Schalter, der alle Fälle behandelt, immer noch wird nichts passieren ... – Mathias
Was ist mit' std :: bitset', um deine Flaggen zu halten? –