函数调用
在C++中,辨别一个函数的方法依次为:作用域→函数名→函数参数,编译器根据这三步来决定调用哪个函数,所以当这三者都无法让编译器决定调用哪个函数时,编译就会出错。如下面这段代码,两个f函数有着相同的作用域和函数名,对f传入一个右值,既能匹配值传递的函数,又能匹配右值引用,所以编译就会报错。
#include <iostream>
using namespace std;
void f(int a) {
cout << "" << endl;
}
void f(int&& a) {
cout << "&" << endl;
}
int main()
{
f(1);
return 0;
}
当编译器能通过这三种方法决定调用哪个函数时,便可正常编译,如将上述代码中对f的调用改为如下:
int a = 1;
f(a);
编译运行正常,因为当传入一个左值时,该参数只能匹配到值传递的f函数。
函数重载
从函数调用的角度上,我认为可以这样理解函数重载:对于两个及以上的函数,编译器必须通过函数参数这最后一步将他们区分,且至少存在一种情况编译器能够区分他们,则这些函数构成重载。
从这个角度上看,函数重载需要满足以下条件:
- 在同一作用域内
- 函数名相同
- 存在编译器能够区分不同函数的情况
所以,第一段代码在调用时虽然会报错,但仍然构成重载,因为存在第二段代码所示的情况能够区分他们。
相反,下面这段代码不构成重载,因为无论何种情况,编译器都无法决定应该调用哪个函数:
void f(int a) {
cout << "" << endl;
}
void f(const int a) {
cout << "const" << endl;
}
而这段代码又构成重载,因为在传入右值时编译器只能匹配第二个f,并且当传入左值引用/常左值引用时编译器也能知道应该调用哪个函数:
void f(int& a) {
cout << "&" << endl;
}
void f(const int& a) {
cout << "const&" << endl;
}
同理,下面这段代码同样可以构成重载:
void f(int* a) {
cout << "*" << endl;
}
void f(const int* a) {
cout << "const*" << endl;
}
对于类成员函数,同样可以采用这种思路进行理解。
例如,对于成员函数和常成员函数,即便他们有相同的参数列表,他们也可以构成重载,因为编译器能够在调用的时候区分他们。
class A {
public:
void f(int a) {
cout << "A::f(int)" << endl;
}
void f(int a) const {
cout << "A::f(int) const" << endl;
}
};
int main()
{
const A a;
a.f(1);
return 0;
}
普通成员函数可以看做增加了一个this指针作为第一个参数的成员函数,而常成员函数的这个this指针是带const的,所以也会导致参数列表不同。
但是相反,具有相同参数列表的静态成员函数和非静态成员函数却不能构成重载,例如以下代码编译便会报错:
class A {
public:
static void f(int a);
void f(int a) {
cout << "A::f(int)" << endl;
}
};
void A::f(int a) {
cout << "static A::f(int)" << endl;
}
原因是静态成员函数既可以通过类调用,也可以通过对象来进行调用,这就导致了当使用对象调用静态成员函数时,若存在有着相同参数列表的非静态成员函数,编译器便无法决定应该调用哪一个,也就是仅仅凭借static无法作为重载他们的依据,但这样换来的好处是——可以重载具有不同参数列表的静态和非静态成员函数。
这样解释就感觉容易理解多了^_^
补充
有一段话写的非常好,函数名的作用是对函数的功能进行提示,而参数列表的功能是对函数的用法进行提示,重载的作用是扩展原有的功能。重载并非必须,而更多的是为了符合人们的使用习惯,为了方便。