隐式用户定义的转换很麻烦

时间:2019-07-08 16:17:14

标签: c++ implicit-conversion

我有两个类:CoolEnum,这是尝试将enum 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类并避免使用它。

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

  1. Wrapper转换为CoolEnum并进行比较

  2. Wrapper转换为Impl并进行比较

  3. ...(可能是其他人)

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

我有两个问题:

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

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

谢谢!

1 个答案:

答案 0 :(得分:2)

  

为什么我会出现编译错误?

在转换序列中只允许您进行一个用户定义的转换。当你做

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

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

  

CoolEnum类的最佳解决方案是什么?

只需为其添加一个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;
}

让其编译