代码之家  ›  专栏  ›  技术社区  ›  Casey

重构作用域枚举按位运算符代码重复

  •  0
  • Casey  · 技术社区  · 5 年前

    ScopedEnumFoo& operator|=(ScopedEnumFoo& a, const ScopedEnumFoo& b) noexcept {
        using underlying = std::underlying_type_t<ScopedEnumFoo>;
        auto underlying_a = static_cast<underlying>(a);
        auto underlying_b = static_cast<underlying>(b);
        a = static_cast<ScopedEnumFoo>(underlying_a | underlying_b);
        return a;
    }
    
    ScopedEnumFoo operator|(ScopedEnumFoo a, const ScopedEnumFoo& b) noexcept {
        a |= b;
        return a;
    }
    
    ScopedEnumFoo& operator&=(ScopedEnumFoo& a, const ScopedEnumFoo& b) noexcept {
        using underlying = std::underlying_type_t<ScopedEnumFoo>;
        auto underlying_a = static_cast<underlying>(a);
        auto underlying_b = static_cast<underlying>(b);
        a = static_cast<ScopedEnumFoo>(underlying_a & underlying_b);
        return a;
    }
    
    ScopedEnumFoo operator&(ScopedEnumFoo a, const ScopedEnumFoo& b) noexcept {
        a &= b;
        return a;
    }
    
    

    除了作用域枚举的类型之外,每个需要用作标志类型的类型的代码都是相同的。这会导致代码质量检查器抛出我已经复制了十几次(或更多)代码的嘶嘶声。

    我该如何去“消除重复”代码?有可能吗?

    作为杰森·特纳 recently

    “我不会复制粘贴代码”是编写好代码最重要的一件事。。。

    0 回复  |  直到 5 年前
        1
  •  1
  •   bolov    5 年前

    template <class E>
    E& operator|=(E& a, const E& b) noexcept {
        using underlying = std::underlying_type_t<E>;
        auto underlying_a = static_cast<underlying>(a);
        auto underlying_b = static_cast<underlying>(b);
        a = static_cast<E>(underlying_a | underlying_b);
        return a;
    }
    

    问题是它很乐意接受 任何类型 并且会以通常意想不到的方式造成麻烦和干扰代码的其他部分。我强烈建议不要使用这个版本,即使操作符位于名称空间后面。

    所以它需要 受限制的 只是想要的类型。我将使用概念,因为它们更有表现力。对于C++前20,很容易转换成经典的SIFAE技术。

    一个快速解决方案是只接受枚举:

    template <class E>
        requires std::is_enum_v<E>
    E& operator|=(E& a, const E& b) noexcept {
        using underlying = std::underlying_type_t<E>;
        auto underlying_a = static_cast<underlying>(a);
        auto underlying_b = static_cast<underlying>(b);
        a = static_cast<E>(underlying_a | underlying_b);
        return a;
    }
    

    template <class E> struct IsAwesomeEnum: std::false_type {};
    
    template <> struct IsAwesomeEnum<ScopedEnumFoo>  : std::true_type {};
    template <> struct IsAwesomeEnum<ScopedEnumBar>  : std::true_type {};
    
    template <class E>
        requires IsAwesomeEnum<E>::value
    E& operator|=(E& a, const E& b) noexcept {
        using underlying = std::underlying_type_t<E>;
        auto underlying_a = static_cast<underlying>(a);
        auto underlying_b = static_cast<underlying>(b);
        a = static_cast<E>(underlying_a | underlying_b);
        return a;
    }
    

    我会更进一步,为它定义一个概念:

    template <class E>
    concept AwesomeEnum = IsAwesomeEnum<E>::value;
    
    template <AwesomeEnum E>
    E& operator|=(E& a, const E& b) noexcept {
        using underlying = std::underlying_type_t<E>;
        auto underlying_a = static_cast<underlying>(a);
        auto underlying_b = static_cast<underlying>(b);
        a = static_cast<E>(underlying_a | underlying_b);
        return a;
    }
    

    为了完整起见,这里有一种非概念SFINAE方法:

    template <class E, class = std::enable_if_t<IsAwesomeEnum<E>::value>>
    E& operator|=(E& a, const E& b) noexcept {
        using underlying = std::underlying_type_t<E>;
        auto underlying_a = static_cast<underlying>(a);
        auto underlying_b = static_cast<underlying>(b);
        a = static_cast<E>(underlying_a | underlying_b);
        return a;
    }
    
    推荐文章