隐式用户定义转换的问题

Trouble with implicit user-defined conversions

我有两个classes:CoolEnum,试图用方法把enum class变成真正的class; template <class WrapMe> class Wrapper;,可隐式转换为 const WrapMe&

我想出了以下实现:

#include <cassert>

class CoolEnum {
public:
    enum Impl {
        kFirst, kSecond
    };

    explicit CoolEnum(Impl value) : value_(value) {
    }

    operator Impl() const {
        return value_;
    }

    CoolEnum Inverse() const {
        switch (value_) {
            case kFirst:
                return CoolEnum{kSecond};
            default:
                return CoolEnum{kFirst};
        }
    }

private:
    Impl value_;
};

template <class WrapMe>
class Wrapper {
public:
    Wrapper(WrapMe value, char other_info)
        : value_(value), other_info_(other_info) {
    }

    operator const WrapMe&() const {
        return value_;
    }

private:
    WrapMe value_;
    char other_info_;
};

int main() {
    // compiles
    assert(CoolEnum(CoolEnum::kFirst) == CoolEnum::kFirst);

    // does not compile: no match for operator ==
    assert(CoolEnum(CoolEnum::kFirst)
       == Wrapper<CoolEnum>(CoolEnum(CoolEnum::kFirst), 'e'));
    return 0;
}

我当然可以 static_cast Wrapper<CoolEnum>CoolEnum,但我相信可以修复 CoolEnum class 并避免它。

我知道的解决方案之一是从 CoolEnum 中删除 operator Impl,我猜这是因为它会导致歧义(尽管我不完全理解为什么)。详细来说,我相信 operator ==:

有几种可能性
  1. Wrapper转换为CoolEnum并比较

  2. Wrapper转换为Impl并比较

  3. ...(也许其他)

但它似乎应该被允许——所有这些都是编译器生成的,并导致相同的结果

我有两个问题:

  1. 为什么会出现编译错误?

  2. CoolEnum class 的最佳解决方案是什么?

谢谢!

Why exactly do I get a compilation error?

您只能在一个转换序列中进行一次用户定义的转换。当你这样做时

CoolEnum(CoolEnum::kFirst) == Wrapper<CoolEnum>(CoolEnum(CoolEnum::kFirst)

CoolEnum(CoolEnum::kFirst) 可以一步转换为 CoolEnum::Impl,但 Wrapper<CoolEnum>(CoolEnum(CoolEnum::kFirst) 首先必须将 Wrapper 转换为 CoolEnum,然后将其转换为 CoolEnum::Impl。因为那是两个用户定义的转换,所以你会得到一个错误

What is the best possible fix for CoolEnum class?

只需为其添加一个operator ==。然后您可以在那里比较枚举值。这将起作用,因为它只需要一个用户定义的转换就可以从 Wrapper<T>T。将代码更改为

#include <cassert>

class CoolEnum {
public:
    enum Impl {
        kFirst, kSecond
    };

    explicit CoolEnum(Impl value) : value_(value) {
    }

    operator Impl() const {
        return value_;
    }

    CoolEnum Inverse() const {
        switch (value_) {
            case kFirst:
                return CoolEnum{kSecond};
            default:
                return CoolEnum{kFirst};
        }
    }
    friend bool operator ==(const CoolEnum& lhs, const CoolEnum& rhs);
    // ^^^^^^^^^^^^^^^^^ added this ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

private:
    Impl value_;
};

bool operator ==(const CoolEnum& lhs, const CoolEnum& rhs)
{
    return lhs.value_ == rhs.value_;
}
// ^^^^^^^^^^^^^^^^^ added this ^^^^^^^^^^^^^^^^^^^^^^^^^^

template <class WrapMe>
class Wrapper {
public:
    Wrapper(WrapMe value, char other_info)
        : value_(value), other_info_(other_info) {
    }

    operator const WrapMe&() const {
        return value_;
    }

private:
    WrapMe value_;
    char other_info_;
};

int main() {
    // compiles
    assert(CoolEnum(CoolEnum::kFirst) == CoolEnum::kFirst);

    // does not compile: no match for operator ==
    assert(CoolEnum(CoolEnum::kFirst)
       == Wrapper<CoolEnum>(CoolEnum(CoolEnum::kFirst), 'e'));
    return 0;
}

让它编译