今日看侯捷老师的C++课程,里面提到了void imag(const double& im) {}void imag(const double im) {}具有同样的函数签名,所以不能进行重载,但在我实际测试中,发现编译器是允许通过传入参数是引用或值来构成重载的,遂进行了一些测试并记录一下。

以下测试环境均为VS2019编译器。

普通函数通过引用构成重载

本来从直觉上来说我也是赞同侯捷老师的看法,因为只通过引用区分函数在函数调用的时候会出现二义性,应该不允许该重载操作才对。因此写了下述两个函数:

1
2
3
4
5
6
7
8
9
void func1(int&)
{
cout << "func(int&)" << endl;
}

void func1(int)
{
cout << "func(int)" << endl;
}

编译发现编译器没有报错,编译器允许了这种重载方式。但是在尝试调用的过程中发现如果传入参数为变量的情况下,编译不通过。

1
2
3
4
5
6
int main(){
int a = 0;
func1(0); //数字作为参数,调用的是非引用版本,打印func(int);
//func1(a); //如果是传入变量,编译器会报错提示“func1”: 对重载函数的调用不明确
return 0;
}

如果想要调用引用版本的func1,有两种方式

1. 通过函数指针的方式明确调用的函数

1
2
3
4
5
6
int main(){
int a = 0;
void (*p)(int&) = func1;
p(a);
return 0;
}

2. 通过static_cast对函数类型进行转换

1
2
3
4
5
int main(){
int a = 0;
static_cast<void(*)(int&)>(func1)(a);
return 0;
}

通过这种方式也可以指定调用非引用版本的func1。

类成员函数通过引用构成重载

对于类成员函数来说,同样的方法也可以实现调用,不过类成员函数的函数指针写法不一样而已。

1
2
3
4
5
class A {
public:
void func1(int x){ cout << "with value" << endl; }
void func1(int& x) { cout << "with reference" << endl; }
};

上述代码编译器同样不会报错,但不能直接传入变量进行调用。

1
2
3
4
5
6
7
int main(){
int a;
A a1;
a1.func1(1); //不报错,打印with value
//a1.func1(a); //编译器报错,提示“A::func1”: 对重载函数的调用不明确
return 0;
}

同样可以通过两种方式对引用版本进行调用

1. 通过函数指针的方式明确调用的类成员函数

不同点在于类成员函数(无static修饰符)的函数指针调用,需要指定具体的调用对象

1
2
3
4
5
6
7
int main(){
int a;
A a1;
void (A::* p)(int&) = &A::func1;
(a1.*p)(a);
return 0;
}

2. 通过static_cast对类成员函数类型进行转换

同样需要指定调用的对象

1
2
3
4
5
6
int main(){
int a;
A a1;
(a1.*static_cast<void(A::*)(int&)>(&A::func1))(a);
return 0;
}