c++ 2 重载具有类似的转换,具体取决于运算符是成员函数还是全局函数
c++ 2 overloads have similar conversions depending on whether the operator is a member function or a global one
考虑一个简单的向量class实现:
#include <algorithm>
class Vector {
public:
Vector(int _elementsCount)
: elementsCount(_elementsCount)
, elements(new float[_elementsCount])
{}
~Vector() {
delete[] elements;
}
Vector(const Vector& rhs) {
elementsCount = rhs.size();
elements = new float[elementsCount];
for (int i = 0; i < elementsCount; ++i)
(*this)[i] = rhs[i];
}
float& operator [](int i) {
return elements[i];
}
float operator [](int i) const {
return const_cast<Vector&>(*this)[i];
}
int size() const {
return elementsCount;
}
/*// Dot product
float operator *(const Vector& v) {
float res = 0;
for (int i = 0; i < size(); ++i)
res += (*this)[i] * v[i];
return res;
}*/
private:
int elementsCount;
float* elements;
};
// Multiplication by a scalar
Vector operator *(const Vector& v, float k) {
Vector res(v.size());
for (int i = 0; i < v.size(); ++i)
res[i] = v[i] * k;
return res;
}
// Dot product
float operator *(const Vector& v1, const Vector& v2) {
float res = 0;
for (int i = 0; i < std::min(v1.size(), v2.size()); ++i)
res += v1[i] * v2[i];
return res;
}
void main()
{
Vector v(2);
v * 3; // ambiguous
}
此代码编译通过。但是如果我们取消注释掉class中的*运算符实现,注释掉它的全局实现(点积函数),那么就会出现“'Vector::operator *': 2 overloads have similar conversions”的错误,因为这里有歧义: 是通过标量调用乘法还是将 3 解释为参数化构造函数的参数并调用点积。这是有道理的。但我不明白将 * 运算符声明为成员函数或全局函数有什么区别。我以为他们在上面的例子中应该是一样的,但事实并非如此。
已添加。我最感兴趣的不是如何避免歧义,而是为什么在一种情况下(当*被声明为成员时)会出现歧义,并且另一个中没有一个(当 * 声明为全局函数时)。
你需要让你的构造函数explicit
:
explicit Vector(int _elementsCount) { ... }
产生歧义的原因是编译器无法决定是否应该将 int
值隐式转换为 Vector
并调用 Vector::operator*
,或者隐式转换 Vector::operator*
=14=] 值到 float
并使用 operator*(const Vector&, float)
.
通过使用explicit
,这样的转换是被禁止的,如果你想让“3”变成Vector
,你必须使用Vector(3)
。
作为旁注,您应该将运算符设为 const
,因为它不会修改对象。使它成为 const 也将允许它与 const Vector
:
一起使用
float operator *(const Vector& v) const { ... }
注意这仍然会与您的其他重载冲突:
float operator *(const Vector& v1, const Vector& v2)
没有理由两者兼得。选择成员函数或全局函数并删除另一个。
考虑一个简单的向量class实现:
#include <algorithm>
class Vector {
public:
Vector(int _elementsCount)
: elementsCount(_elementsCount)
, elements(new float[_elementsCount])
{}
~Vector() {
delete[] elements;
}
Vector(const Vector& rhs) {
elementsCount = rhs.size();
elements = new float[elementsCount];
for (int i = 0; i < elementsCount; ++i)
(*this)[i] = rhs[i];
}
float& operator [](int i) {
return elements[i];
}
float operator [](int i) const {
return const_cast<Vector&>(*this)[i];
}
int size() const {
return elementsCount;
}
/*// Dot product
float operator *(const Vector& v) {
float res = 0;
for (int i = 0; i < size(); ++i)
res += (*this)[i] * v[i];
return res;
}*/
private:
int elementsCount;
float* elements;
};
// Multiplication by a scalar
Vector operator *(const Vector& v, float k) {
Vector res(v.size());
for (int i = 0; i < v.size(); ++i)
res[i] = v[i] * k;
return res;
}
// Dot product
float operator *(const Vector& v1, const Vector& v2) {
float res = 0;
for (int i = 0; i < std::min(v1.size(), v2.size()); ++i)
res += v1[i] * v2[i];
return res;
}
void main()
{
Vector v(2);
v * 3; // ambiguous
}
此代码编译通过。但是如果我们取消注释掉class中的*运算符实现,注释掉它的全局实现(点积函数),那么就会出现“'Vector::operator *': 2 overloads have similar conversions”的错误,因为这里有歧义: 是通过标量调用乘法还是将 3 解释为参数化构造函数的参数并调用点积。这是有道理的。但我不明白将 * 运算符声明为成员函数或全局函数有什么区别。我以为他们在上面的例子中应该是一样的,但事实并非如此。
已添加。我最感兴趣的不是如何避免歧义,而是为什么在一种情况下(当*被声明为成员时)会出现歧义,并且另一个中没有一个(当 * 声明为全局函数时)。
你需要让你的构造函数explicit
:
explicit Vector(int _elementsCount) { ... }
产生歧义的原因是编译器无法决定是否应该将 int
值隐式转换为 Vector
并调用 Vector::operator*
,或者隐式转换 Vector::operator*
=14=] 值到 float
并使用 operator*(const Vector&, float)
.
通过使用explicit
,这样的转换是被禁止的,如果你想让“3”变成Vector
,你必须使用Vector(3)
。
作为旁注,您应该将运算符设为 const
,因为它不会修改对象。使它成为 const 也将允许它与 const Vector
:
float operator *(const Vector& v) const { ... }
注意这仍然会与您的其他重载冲突:
float operator *(const Vector& v1, const Vector& v2)
没有理由两者兼得。选择成员函数或全局函数并删除另一个。