隐式转换未按预期工作

时间:2017-11-10 20:53:35

标签: c++ c++11 implicit-conversion

我编写了一个简单的Flags类,但是我的运算符定义遇到了问题。好像我依赖于一些不存在的隐式转换规则。

enum class SomeEnum { ONE, TWO, THREE };

template<typename Enum>
class Flags {
    Flags(const Flags& f) { };
    Flags(Enum e) { };

    Flags& operator |=(Flags<Enum> f) {
        //...;
        return *this;
    }
};

template<typename Enum>
Flags<Enum> operator |(Enum a, Flags<Enum> b) { return b | a; }

template<typename Enum>
Flags<Enum> operator |(Flags<Enum> a, Enum b) { return a |= b; }

int main() {
    Flags<SomeEnum> flags = SomeEnum::ONE | SomeEnum::TWO;
    return 0;
}

编译时我收到此错误:

implicit.cpp: In function ‘int main()’:  
implicit.cpp:26:40: error: no match for ‘operator|’ (operand types are ‘SomeEnum’ and ‘SomeEnum’)  
  Flags<SomeEnum> flags = SomeEnum::ONE | SomeEnum::TWO;

我的理解是,SomeEnum中的一个被隐式转换为Flags<Enum>,然后传递给正确的运算符。我错过了什么规则?

修改

我查看了https://stackoverflow.com/questions/9787593,但建议的解决方案(非会员朋友运营商)没有解决我的问题。 我确实删除了全局定义并添加了这些成员:

friend Flags operator |(Enum a, Flags b) { return b | a; }
friend Flags operator |(Flags a, Enum b) { return a |= b; }

但错误仍然相同(live demo)。

1 个答案:

答案 0 :(得分:3)

为了知道SomeEnum可转换为Flags<SomeEnum>,它必须已将Enum模板参数推断为SomeEnum,但它不能从参数中推导出来,因为它们都不匹配Flags<Enum>

即。模板参数推导必须在转换到另一种类型之前进行检查。

您可以调整功能,以便只有一个参数参与参数推断:

template<typename T> struct nondeduced { using type = T; }

template<typename Enum>
Flags<Enum> operator |(Enum a, Flags<typename nondeduced<Enum>::type> b)

或等效地:

template<typename E> struct FlagType { using type = Flags<E>; }

template<typename Enum>
Flags<Enum> operator |(Enum a, typename FlagType<Enum>::type b);

这在非推导的上下文中使用Enum模板参数,因此只有另一个参数用于演绎。

但是在这之后你现在有了模糊的重载,因为编译器无法知道你是想要转换第一个参数,还是第二个参数。

您需要添加一个需要两种SomeEnum类型的重载,并明确执行转化为Flags<SomeEnum>。如果枚举类型应该与operator|一起使用,那么它应该定义该运算符本身。

另一个不涉及更改枚举类型的选项是添加一个帮助器,将枚举器转换为Flags对象:

template<typename Enum>
inline Flags<Enum> flags(Enum e) { return Flags<Enum>(e); }

然后你可以说SomeEnum::TWO | flags(SomeEnum::TWO)避免歧义。

作为一个风格点,don't use ALL_CAPS for enumerators