今天,我有一些时间,并试图改进我自己的Bit掩码适配器类。
通常的做法是用类似于标志系统的方法来消除布尔变量的集合。传统的方法是创建枚举,并使用一些位操作将这些值存储在unsigned int中。这不是一项非常复杂的任务,但至少容易出错,乏味,可读性差。随着枚举类的增加,我们得到了更多的类型安全性,我使用。最后,这个类是一些位操作的简单包装器,它完成了解释的任务。
我在很多项目中都使用这个功能,因此我的目标是尽可能好地实现它。当然,这并不是世界上最重的课程之一,但是也许有人会有一些很好的调整,这会让它变得更好。
正如您可能在折叠表达式上看到的那样,我已经更新到c++17,因此可以随意建议在最新标准范围内进行任何改进。
最后,我对任何关于可用性和进一步改进的意见都感兴趣;)
#include <type_traits>
namespace
{
template <class FlagType, class... Args>
using enable_if_all_same_as_T = std::enable_if_t<std::is_same_v<std::common_type_t<Args...>, FlagType>>;
template <class T, class... Args>
constexpr T combine(Args... _args) noexcept
{
T mask = (... | static_cast<T>(_args));
return mask;
}
}
template <class FlagType, class StorageType = unsigned int>
class Bitmask
{
static_assert(std::is_enum_v<FlagType> || (std::is_integral_v<FlagType> && std::is_unsigned_v<FlagType>), "FlagType must be an unsigned integer or enum type.");
static_assert(std::is_integral_v<StorageType> && std::is_unsigned_v<StorageType>, "StorageType must be an unsigned integer type.");
public:
constexpr explicit Bitmask() noexcept = default;
template <class... Args, typename = enable_if_all_same_as_T<FlagType, Args...>>
constexpr explicit Bitmask(Args... _args) noexcept :
m_Mask(combine<StorageType>((_args, ...)))
{}
constexpr Bitmask(const Bitmask& _other) noexcept = default;
constexpr Bitmask& operator =(const Bitmask& _other) noexcept = default;
constexpr StorageType operator*() const noexcept
{
return m_Mask;
}
template <class... Args, typename = enable_if_all_same_as_T<FlagType, Args...>>
constexpr bool contains(Args... _args) const noexcept
{
auto mask = combine<StorageType>((_args, ...));
return (m_Mask & mask) == mask;
}
template <class... Args, typename = enable_if_all_same_as_T<FlagType, Args...>>
constexpr void apply(Args... _args) noexcept
{
m_Mask |= combine<StorageType>((_args, ...));
}
template <class... Args, typename = enable_if_all_same_as_T<FlagType, Args...>>
constexpr void remove(Args... _args) noexcept
{
m_Mask &= ~combine<StorageType>((_args, ...));
}
constexpr Bitmask& operator &=(const Bitmask& _mask) noexcept
{
m_Mask &= _mask.m_Mask;
return *this;
}
constexpr Bitmask& operator |=(const Bitmask& _mask) noexcept
{
m_Mask |= _mask.m_Mask;
return *this;
}
constexpr Bitmask& operator ^=(const Bitmask& _mask) noexcept
{
m_Mask ^= _mask.m_Mask;
return *this;
}
private:
StorageType m_Mask = 0;
};
template <class FlagType, class StorageType>
constexpr Bitmask<FlagType, StorageType> operator &(const Bitmask<FlagType, StorageType>& _lhs, const Bitmask<FlagType, StorageType>& _rhs) noexcept
{
auto tmp(_lhs);
tmp &= _rhs;
return tmp;
}
template <class FlagType, class StorageType>
constexpr Bitmask<FlagType, StorageType> operator |(const Bitmask<FlagType, StorageType>& _lhs, const Bitmask<FlagType, StorageType>& _rhs) noexcept
{
auto tmp(_lhs);
tmp |= _rhs;
return tmp;
}
template <class FlagType, class StorageType>
constexpr Bitmask<FlagType, StorageType> operator ^(const Bitmask<FlagType, StorageType>& _lhs, const Bitmask<FlagType, StorageType>& _rhs) noexcept
{
auto tmp(_lhs);
tmp ^= _rhs;
return tmp;
}
template <class FlagType, class StorageType>
constexpr bool operator ==(const Bitmask<FlagType, StorageType>& _lhs, const Bitmask<FlagType, StorageType>& _rhs) noexcept
{
return *_lhs == *_rhs;
}
template <class FlagType, class StorageType>
constexpr bool operator !=(const Bitmask<FlagType, StorageType>& _lhs, const Bitmask<FlagType, StorageType>& _rhs) noexcept
{
return !(_lhs == _rhs);
}
int main()
{
enum class Test
{
a = 0b0001,
b = 0b0010,
c = 0b0100,
d = 0b1000,
some = c | a,
all = a | b | c | d
};
Bitmask<Test> mask(Test::a, Test::b, Test::all);
auto other(mask);
if (mask.contains(Test::some))
{
mask.remove(Test::a);
if (mask.contains(Test::some))
{
mask.remove(Test::all);
}
}
other ^= mask;
auto other2 = mask | other;
auto check = other != mask;
}发布于 2018-06-01 14:27:43
对enable_if_all_same_as_T的测试似乎有点过于严格--当然所有的参数都只需要分配给FlagType,而不是完全相同的?这在初始化列表构造函数中是可以实现的:
constexpr explicit Bitmask(std::initializer_list<FlagType> args) noexcept;是的,这意味着你不能再玩折叠表达,但这不应该是一个目标。仅仅因为你有锤子,你就不应该以为一切都是钉子!
在免费操作符中,如果您按值传递lhs,则可以避免复制它。如果给你一个rvalue,这就节省了一个不必要的副本(对于lvalue,它只是移动到完成必要复制的地方)。例如:
template <class FlagType, class StorageType>
constexpr Bitmask<FlagType, StorageType>
operator^(Bitmask<FlagType, StorageType> lhs,
const Bitmask<FlagType, StorageType>& rhs) noexcept
{
return lhs ^= rhs;
}https://codereview.stackexchange.com/questions/195632
复制相似问题