编译器如何知道是使用成员运算符重载还是全局运算符重载?
How does the compiler know whether to use a member operator overload or a global operator overloads?
我有一个关于 C++ 运算符的问题,希望能在这里找到答案。
问题的简短版本在标题中,但如果对我真正要问的内容有任何疑问,请查看完整版本。
c++ 运算符可以重载,这样就可以编写如下内容:
MyClass a(1), b(2);
Myclass c = a + b;
据我了解,典型的实现方式如下所示:
class MyClass
{
private:
int val;
public:
explicit MyClass(int _val);
MyClass operator+(MyClass const& other) const;
MyClass operator+(int i) const;
};
在这种情况下还包括类型为 int
的重载,这使得编写如下内容成为可能:
MyClass a(1);
Myclass b = a + 2;
但不是这样的:
MyClass a(1);
Myclass b = 2 + a;
因为这就像调用 2.operator+(a)
,而 2
不是 object。由于程序员希望以使其成为可能的方式重载运算符,因此有第二种实现它们的方法,如下所示:
class MyClass
{
private:
int val;
public:
explicit MyClass(int _val);
friend MyClass operator+(MyClass const& lhs, MyClass const& rhs);
friend MyClass operator+(int lhs, MyClass const& rhs);
friend MyClass operator+(MyClass const& lhs, int rhs);
};
允许所有三种类型的添加。
现在,困扰我的是:如果我们同时实现两者会怎样?编译器如何决定是使用成员运算符还是全局运算符?
公平地说,调用哪个运算符在任何合理的实现中都不重要,它们既不应该 return 不同的东西也不应该有不同的副作用,但我尝试实现它以查看会发生什么:
class MyClass
{
private:
int val;
public:
explicit MyClass(int _val) : val(_val){}
MyClass operator+(MyClass const& other) const
{
cout << "Call to member operator+ for MyClass+MyClass" << endl;
return MyClass(val + other.val);
}
MyClass operator+(int other) const
{
cout << "Call to member operator+ for MyClass+int" << endl;
return MyClass(val + other);
}
friend MyClass operator+(MyClass const& lhs, MyClass const& rhs)
{
cout << "Call to global operator+ for MyClass+MyClass " << endl;
return MyClass(lhs.val + rhs.val);
}
friend MyClass operator+(int lhs, MyClass const& rhs)
{
cout << "Call to global operator+ for int+MyClass " << endl;
return MyClass(lhs + rhs.val);
}
friend MyClass operator+(MyClass const& lhs, int rhs)
{
cout << "Call to global operator+ for MyClass+int " << endl;
return MyClass(lhs.val + rhs);
}
};
int main() {
MyClass a(1), b(2);
int i(3);
MyClass r_0 = a.operator+(b);
MyClass r_1 = a.operator+(i);
MyClass r_2 = operator+(a,b);
MyClass r_3 = operator+(a,i);
MyClass r_4 = operator+(i,a);
MyClass r_5 = a + b;
MyClass r_6 = a + i;
MyClass r_7 = i + a;
return 0;
}
编译并打印
Call to member operator+ for MyClass+MyClass
Call to member operator+ for MyClass+int
Call to global operator+ for MyClass+MyClass
Call to global operator+ for MyClass+int
Call to global operator+ for int+MyClass
Call to global operator+ for MyClass+MyClass
Call to global operator+ for MyClass+int
Call to global operator+ for int+MyClass
我很想认为这里面的一切都是合法的,并且全球运营商优先于成员运营商,但我在网上唯一能找到的似乎表明这些添加是模棱两可的调用,所以真的是这样还是我只是在看未定义的行为?
成员函数和非成员函数平等参与重载决议。为了使它们具有可比性,每个成员函数都由编译器使用隐式对象参数进行扩展。
([over.match.funcs]/p2):
The set of candidate functions can contain both member and non-member functions to be resolved against the same argument list. So that argument and parameter lists are comparable within this heterogeneous set, a member function is considered to have an extra first parameter, called the implicit object parameter, which represents the object for which the member function has been called. For the purposes of overload resolution, both static and non-static member functions have an implicit object parameter, but constructors do not.
During overload resolution, the implied object argument is indistinguishable from other arguments. The implicit object parameter, however, retains its identity since no user-defined conversions can be applied to achieve a type match with it.
鉴于隐式对象参数也继承了非静态成员函数的ref-和cv-限定,这基本上意味着从编译器的角度来看,成员运算符声明为:
MyClass MyClass::operator+(int) const;
在某种程度上等同于:
MyClass operator+(const MyClass&, int);
与常规非成员函数不同的唯一例外是第一个(隐式对象)参数不考虑用户定义的转换(这就是为什么1 + a
永远不会使用某些转换构造函数 A(int)
将 1
转换为 A
以调用 A::operator+(const A&)
),并且临时实例 可以 被为非常量限定成员函数生成的非常量引用绑定。
成员和全局 operator+
在您的代码中不明确,应该产生这样的错误。
我有一个关于 C++ 运算符的问题,希望能在这里找到答案。 问题的简短版本在标题中,但如果对我真正要问的内容有任何疑问,请查看完整版本。
c++ 运算符可以重载,这样就可以编写如下内容:
MyClass a(1), b(2);
Myclass c = a + b;
据我了解,典型的实现方式如下所示:
class MyClass
{
private:
int val;
public:
explicit MyClass(int _val);
MyClass operator+(MyClass const& other) const;
MyClass operator+(int i) const;
};
在这种情况下还包括类型为 int
的重载,这使得编写如下内容成为可能:
MyClass a(1);
Myclass b = a + 2;
但不是这样的:
MyClass a(1);
Myclass b = 2 + a;
因为这就像调用 2.operator+(a)
,而 2
不是 object。由于程序员希望以使其成为可能的方式重载运算符,因此有第二种实现它们的方法,如下所示:
class MyClass
{
private:
int val;
public:
explicit MyClass(int _val);
friend MyClass operator+(MyClass const& lhs, MyClass const& rhs);
friend MyClass operator+(int lhs, MyClass const& rhs);
friend MyClass operator+(MyClass const& lhs, int rhs);
};
允许所有三种类型的添加。
现在,困扰我的是:如果我们同时实现两者会怎样?编译器如何决定是使用成员运算符还是全局运算符?
公平地说,调用哪个运算符在任何合理的实现中都不重要,它们既不应该 return 不同的东西也不应该有不同的副作用,但我尝试实现它以查看会发生什么:
class MyClass
{
private:
int val;
public:
explicit MyClass(int _val) : val(_val){}
MyClass operator+(MyClass const& other) const
{
cout << "Call to member operator+ for MyClass+MyClass" << endl;
return MyClass(val + other.val);
}
MyClass operator+(int other) const
{
cout << "Call to member operator+ for MyClass+int" << endl;
return MyClass(val + other);
}
friend MyClass operator+(MyClass const& lhs, MyClass const& rhs)
{
cout << "Call to global operator+ for MyClass+MyClass " << endl;
return MyClass(lhs.val + rhs.val);
}
friend MyClass operator+(int lhs, MyClass const& rhs)
{
cout << "Call to global operator+ for int+MyClass " << endl;
return MyClass(lhs + rhs.val);
}
friend MyClass operator+(MyClass const& lhs, int rhs)
{
cout << "Call to global operator+ for MyClass+int " << endl;
return MyClass(lhs.val + rhs);
}
};
int main() {
MyClass a(1), b(2);
int i(3);
MyClass r_0 = a.operator+(b);
MyClass r_1 = a.operator+(i);
MyClass r_2 = operator+(a,b);
MyClass r_3 = operator+(a,i);
MyClass r_4 = operator+(i,a);
MyClass r_5 = a + b;
MyClass r_6 = a + i;
MyClass r_7 = i + a;
return 0;
}
编译并打印
Call to member operator+ for MyClass+MyClass
Call to member operator+ for MyClass+int
Call to global operator+ for MyClass+MyClass
Call to global operator+ for MyClass+int
Call to global operator+ for int+MyClass
Call to global operator+ for MyClass+MyClass
Call to global operator+ for MyClass+int
Call to global operator+ for int+MyClass
我很想认为这里面的一切都是合法的,并且全球运营商优先于成员运营商,但我在网上唯一能找到的似乎表明这些添加是模棱两可的调用,所以真的是这样还是我只是在看未定义的行为?
成员函数和非成员函数平等参与重载决议。为了使它们具有可比性,每个成员函数都由编译器使用隐式对象参数进行扩展。 ([over.match.funcs]/p2):
The set of candidate functions can contain both member and non-member functions to be resolved against the same argument list. So that argument and parameter lists are comparable within this heterogeneous set, a member function is considered to have an extra first parameter, called the implicit object parameter, which represents the object for which the member function has been called. For the purposes of overload resolution, both static and non-static member functions have an implicit object parameter, but constructors do not.
During overload resolution, the implied object argument is indistinguishable from other arguments. The implicit object parameter, however, retains its identity since no user-defined conversions can be applied to achieve a type match with it.
鉴于隐式对象参数也继承了非静态成员函数的ref-和cv-限定,这基本上意味着从编译器的角度来看,成员运算符声明为:
MyClass MyClass::operator+(int) const;
在某种程度上等同于:
MyClass operator+(const MyClass&, int);
与常规非成员函数不同的唯一例外是第一个(隐式对象)参数不考虑用户定义的转换(这就是为什么1 + a
永远不会使用某些转换构造函数 A(int)
将 1
转换为 A
以调用 A::operator+(const A&)
),并且临时实例 可以 被为非常量限定成员函数生成的非常量引用绑定。
成员和全局 operator+
在您的代码中不明确,应该产生这样的错误。