隐式用户定义转换的问题
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 ==
:
有几种可能性
将Wrapper
转换为CoolEnum
并比较
将Wrapper
转换为Impl
并比较
...(也许其他)
但它似乎应该被允许——所有这些都是编译器生成的,并导致相同的结果
我有两个问题:
为什么会出现编译错误?
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;
}
让它编译
我有两个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 ==
:
将
Wrapper
转换为CoolEnum
并比较将
Wrapper
转换为Impl
并比较...(也许其他)
但它似乎应该被允许——所有这些都是编译器生成的,并导致相同的结果
我有两个问题:
为什么会出现编译错误?
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;
}
让它编译