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

我有几个可以用作按位标志的作用域枚举。我已经为每种类型相同地实现了按位运算符重载,如下例所示:

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;
}

除了有作用域的枚举类型外,对于需要用作标记类型的每种类型,代码都是相同的。这导致代码质量检查人员抛出我已经重复打了十二次(或更多)代码的虚假信息。

我将如何“重复删除”代码?甚至有可能吗?

如Jason Turner recently所说:

“我不会复制粘贴代码”是编写好的代码所能做的最重要的事情...

iCMS 回答:重构作用域枚举按位运算符代码重复

当模板具有相同的功能,它们在不同类型上运行时,第一步就是创建模板:

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之前的版本,可以轻松地转换为经典的SFINAE技术。

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

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;
}
本文链接:https://www.f2er.com/1934468.html

大家都在问