为什么隐式转换对于非原始类型没有歧义?
Why is implicit conversion not ambiguous for non-primitive types?
给定一个具有多个隐式转换函数(非显式构造函数和转换运算符)的简单 class 模板,如下例所示:
template<class T>
class Foo
{
private:
T m_value;
public:
Foo();
Foo(const T& value):
m_value(value)
{
}
operator T() const {
return m_value;
}
bool operator==(const Foo<T>& other) const {
return m_value == other.m_value;
}
};
struct Bar
{
bool m;
bool operator==(const Bar& other) const {
return false;
}
};
int main(int argc, char *argv[])
{
Foo<bool> a (true);
bool b = false;
if(a == b) {
// This is ambiguous
}
Foo<int> c (1);
int d = 2;
if(c == d) {
// This is ambiguous
}
Foo<Bar> e (Bar{true});
Bar f = {false};
if(e == f) {
// This is not ambiguous. Why?
}
}
涉及基本类型 (bool
、int
) 的比较运算符如预期的那样不明确 - 编译器不知道是否应使用转换运算符来转换左侧模板 class 实例到原始类型或使用转换构造函数将右侧原始类型转换为预期的 class 模板实例。
不过,最后的比较,涉及到一个简单的struct
,并没有歧义。为什么?将使用哪个转换函数?
使用编译器 msvc 15.9.7 测试。
根据[over.binary]/1
Thus, for any binary operator @
, x@y
can be interpreted
as either x.operator@(y)
or operator@(x,y)
.
根据这个规则,在e == f
的情况下,编译器只能将其解释为e.operator==(f)
,而不是f.operator==(e)
。所以没有歧义;您定义为 Bar
成员的 operator==
根本不是重载决议的候选对象。
在 a == b
和 c == d
的情况下,内置候选 operator==(int, int)
(参见 [over.built]/13)与 operator==
定义为 Foo<T>
.
的成员
作为成员函数实现的运算符重载不允许隐式转换其左侧操作数,即调用它们的对象。
写出运算符重载的显式调用总是有助于更好地理解它的作用:
Foo<Bar> e (Bar{true});
Bar f = {false};
// Pretty explicit: call the member function Foo<Bar>::operator==
if(e.operator ==(f)) { /* ... */ }
这不能与 Bar
中的比较运算符混淆,因为它需要对左侧进行隐式转换,这是不可能的。
当您像这样定义 Bar
及其比较运算符时,您可能会触发类似于您在内置类型中看到的歧义:
struct Bar { bool m; };
// A free function allows conversion, this will be ambiguous:
bool operator==(const Bar&, const Bar&)
{
return false;
}
Scott Meyers's Effective C++,第 24 项中对此进行了很好的演示和解释。
给定一个具有多个隐式转换函数(非显式构造函数和转换运算符)的简单 class 模板,如下例所示:
template<class T>
class Foo
{
private:
T m_value;
public:
Foo();
Foo(const T& value):
m_value(value)
{
}
operator T() const {
return m_value;
}
bool operator==(const Foo<T>& other) const {
return m_value == other.m_value;
}
};
struct Bar
{
bool m;
bool operator==(const Bar& other) const {
return false;
}
};
int main(int argc, char *argv[])
{
Foo<bool> a (true);
bool b = false;
if(a == b) {
// This is ambiguous
}
Foo<int> c (1);
int d = 2;
if(c == d) {
// This is ambiguous
}
Foo<Bar> e (Bar{true});
Bar f = {false};
if(e == f) {
// This is not ambiguous. Why?
}
}
涉及基本类型 (bool
、int
) 的比较运算符如预期的那样不明确 - 编译器不知道是否应使用转换运算符来转换左侧模板 class 实例到原始类型或使用转换构造函数将右侧原始类型转换为预期的 class 模板实例。
不过,最后的比较,涉及到一个简单的struct
,并没有歧义。为什么?将使用哪个转换函数?
使用编译器 msvc 15.9.7 测试。
根据[over.binary]/1
Thus, for any binary operator
@
,x@y
can be interpreted as eitherx.operator@(y)
oroperator@(x,y)
.
根据这个规则,在e == f
的情况下,编译器只能将其解释为e.operator==(f)
,而不是f.operator==(e)
。所以没有歧义;您定义为 Bar
成员的 operator==
根本不是重载决议的候选对象。
在 a == b
和 c == d
的情况下,内置候选 operator==(int, int)
(参见 [over.built]/13)与 operator==
定义为 Foo<T>
.
作为成员函数实现的运算符重载不允许隐式转换其左侧操作数,即调用它们的对象。
写出运算符重载的显式调用总是有助于更好地理解它的作用:
Foo<Bar> e (Bar{true});
Bar f = {false};
// Pretty explicit: call the member function Foo<Bar>::operator==
if(e.operator ==(f)) { /* ... */ }
这不能与 Bar
中的比较运算符混淆,因为它需要对左侧进行隐式转换,这是不可能的。
当您像这样定义 Bar
及其比较运算符时,您可能会触发类似于您在内置类型中看到的歧义:
struct Bar { bool m; };
// A free function allows conversion, this will be ambiguous:
bool operator==(const Bar&, const Bar&)
{
return false;
}
Scott Meyers's Effective C++,第 24 项中对此进行了很好的演示和解释。