읽는데 약 3분
디폴트 매개변수와 함수 오버로딩
디폴트 매개변수
디폴트 매개변수는 함수 호출에서 실제 매개변수를 생략했을 경우에 실제 매개변수 대신 사용되는 값입니다. 호출 시 매개변수의 개수가 다르더라도 결국 같은 함수를 호출하는 것입니다.
#include <iostream>
using namespace std;
int PLUS(int a, int b = 1) {
return a + b;
}
int main() {
int x = 5;
int y = 7;
cout << PLUS(x, y) << '\n'; // return 12
cout << PLUS(x) << '\n'; // return 6
cout << PLUS(y) << '\n'; // return 8
return 0;
}
디폴트 매개변수를 통해 함수를 작성할 시, 디폴트 매개변수를 넣는 순서는 오른쪽에서 왼쪽 순서가 되어야 한다는 것입니다. 아래 코드는 각 함수의 원형을 제시합니다.
int allSum(int a, int b, int c = 1); // 맞다
int allSum2(int a, int b = 1, int c); // 디폴트 매개변수로 설정된 b 오른쪽에 설정이 되지 않은 c, 틀림
int allSum3(int a = 1, int b = 1,int c = 1);// 맞다
함수 오버로딩
같은 함수를 호출하는 디폴트 매개변수와는 달리, 함수 오버로딩은 서로 다른 여러 개의 함수가 이름을 공유하는 것입니 다. 이렇게 서로 다른 함수들을 구분하는 핵심은 각 함수의 매개변수 리스트입니다. 이를 함수 시그니쳐(function signature)라고 합니다. 여러 예시를 보며 각 시그니쳐가 구분되는지 살펴보겠습니다.
//Case #1: 매개변수 리스트가(int,int) (double,double)로 구분 되므로 서로 다른 시그니쳐
void print(int a, int b);
void print(double a, double b);
//Case #2: 매개변수 리스트가 (int,float)으로 일치하므로 서로 같은 시그니쳐
double ACC(int c, float d);
int ACC(int c, float d);
//Case #3: 매개변수 리스트가 (int,float) (float,int) 로 구분되므로 서로 다른 시그니쳐
double foo(int c, float d);
double foo(float c, int d);
눈 여겨볼 경우는 2번 케이스입니다. return 타입이 다르니 서로 다른 시그니쳐라고 할 수 있지 않나?라고 생각할 수 도있지만 함수 오버로딩을 구분하는 것은 매개변수 리스트임을 상기하도록 합시다. 다음 경우도 살펴봅시다.
// 매개변수가 다르니 서로 다른 시그니처인가?
double sq(double x);
double sq(double& x);
함수 오버로딩은 컴파일러 입장에서 살펴보아야 합니다. $sq$함수를 호출할 때 우리는 호출 함수에서 다음과 같이 작성할 것입니다.
double x = 5.0;
double ans = sq(x);
컴파일러 입장에선 위 두 가지 $sq$함수를 구분할 수 있는 방법이 없습니다. 그러므로 컴파일러가 함수 시그니처를 검사할 땐 어떤 데이터형에 대한 참조와 그 데이터형을 구분하지 않고 같은 시그니처로 간주합니다.
하지만 대응하는 함수를 찾는 과정에서 const와 const가 아닌 변수는 구분합니다.
#include <iostream>
using namespace std;
void Aoo(char* str); //#1
void Aoo(const char* str);//#2
void Boo(char* str); //#3
void Coo(const char* str); //#4
int main() {
const char STR[20] = "Hello, world!";
char* STR2 = "hello, wolrd!";
Aoo(STR); // #2가 오버로딩된다.
Aoo(STR2); // #1가 오버로딩된다.
Boo(STR); // 대응하는 원형이 없다.
Coo(STR2); // #4가 오버로딩된다.
return 0;
}
const 변수와 const가 아닌 변수는 const를 사용한 매개변수에 값을 전달할 수 있지만, const 변수는 const를 사용하지 않은 매개변수에 값을 전달할 수 없다는 것에 유의하도록 합시다.
void foo(double& d); // 변경가능한 lvalue와 매치
void foo(const double& d); //변경가능한 lvalue,const lvalue, rvalue와 매치
void foo(double&& d); //rvalue와 매치
마지막으로 살펴볼 참조 매개변수에서의 오버로딩입니다. 이 상황에서 foo를 오버로딩을 한다면 더 정확한 매치를 얻을 수 있습니다. 세 번째 $foo$함수가 없다면 호출 함수에서 $foo(i+j)$와 같은 호출은 2번째 $foo$를 오버로딩하여 사용합니다.