【发布时间】:2015-03-06 08:22:49
【问题描述】:
我正在尝试使用模板创建类型安全的 C++ 标志。我还想区分 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)
...原因破坏了一切。我想要这样的东西(不是 compilabel 代码)。或者任何其他更好的主意!
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);
}
> DEMO
实际代码会包含更多的运算符,但这足以说明问题。
一个不那么侵入性的解决方案是这样的(但仍然太侵入性)
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) 必须存在,我失去了安全性。另外,我希望只为实际需要的类型创建全局运算符重载。宏也不是神,因为我希望允许在类中声明 EnumFlags。全局重载不能存在,因此我需要在不同位置调用两个宏以在 EnumFlag 上创建。
解决方案必须是纯C++11/stl。
【问题讨论】:
-
不会添加从
EnumFlag<T>到T的非显式转换来解决由EnumFlag<T> operator | (T l, T r)引起的问题吗? -
我认为这是一种解决方法,在不需要时创建
EnumFlag实例,只是为了转换回来。这也将允许使用Options的实例调用foo(const Option &o)。因此,丢失类型安全。在foo内部,我们可能有一个处理所有情况的开关,但仍然不会发生任何事情...... -
std::bitset拿着你的旗帜怎么样?
标签: c++ templates c++11 enums type-safety