首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >位掩码适配器

位掩码适配器
EN

Code Review用户
提问于 2018-06-01 12:50:40
回答 1查看 304关注 0票数 8

今天,我有一些时间,并试图改进我自己的Bit掩码适配器类。

通常的做法是用类似于标志系统的方法来消除布尔变量的集合。传统的方法是创建枚举,并使用一些位操作将这些值存储在unsigned int中。这不是一项非常复杂的任务,但至少容易出错,乏味,可读性差。随着枚举类的增加,我们得到了更多的类型安全性,我使用。最后,这个类是一些位操作的简单包装器,它完成了解释的任务。

我在很多项目中都使用这个功能,因此我的目标是尽可能好地实现它。当然,这并不是世界上最重的课程之一,但是也许有人会有一些很好的调整,这会让它变得更好。

正如您可能在折叠表达式上看到的那样,我已经更新到c++17,因此可以随意建议在最新标准范围内进行任何改进。

最后,我对任何关于可用性和进一步改进的意见都感兴趣;)

代码语言:javascript
复制
#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;
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2018-06-01 14:27:43

enable_if_all_same_as_T的测试似乎有点过于严格--当然所有的参数都只需要分配给FlagType,而不是完全相同的?这在初始化列表构造函数中是可以实现的:

代码语言:javascript
复制
constexpr explicit Bitmask(std::initializer_list<FlagType> args) noexcept;

是的,这意味着你不能再玩折叠表达,但这不应该是一个目标。仅仅因为你有锤子,你就不应该以为一切都是钉子!

在免费操作符中,如果您按值传递lhs,则可以避免复制它。如果给你一个rvalue,这就节省了一个不必要的副本(对于lvalue,它只是移动到完成必要复制的地方)。例如:

代码语言:javascript
复制
template <class FlagType, class StorageType>
constexpr Bitmask<FlagType, StorageType>
operator^(Bitmask<FlagType, StorageType> lhs,
          const Bitmask<FlagType, StorageType>& rhs) noexcept
{
    return lhs ^= rhs;
}
票数 6
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/195632

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档