Проблема с неявными пользовательскими преобразованиями - PullRequest
0 голосов
/ 08 июля 2019

У меня есть два класса: 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. преобразование *От 1029 * до Impl и сравните

  3. ... (возможно, другие)

, но кажется, что это должно быть тривиально разрешено -все они генерируются компилятором и приводят к одному и тому же результату

У меня два вопроса:

  1. Почему именно я получаю ошибку компиляции?

  2. Какое самое лучшее исправление для CoolEnum класса?

Спасибо!

1 Ответ

2 голосов
/ 08 июля 2019

Почему именно я получаю ошибку компиляции?

Вам разрешено только одно пользовательское преобразование в последовательности преобразования. Когда вы делаете

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

CoolEnum(CoolEnum::kFirst) можно преобразовать в CoolEnum::Impl за один шаг, но Wrapper<CoolEnum>(CoolEnum(CoolEnum::kFirst) сначала должен преобразовать Wrapper в CoolEnum, а затем преобразовать его в CoolEnum::Impl. Поскольку это два пользовательских преобразования, вы получаете ошибку

Какое самое лучшее исправление для класса CoolEnum?

Просто добавьте operator == для него. Затем вы можете сделать сравнение значения enum там. Это будет работать, потому что для перехода от 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;
}

позволяет компилировать

...