Default arguments
함수가 하나 이상의 후행 인수를 제공하지 않고 호출될 수 있도록 합니다.
함수 선언의 매개변수 목록 에서 매개변수에 대해 다음 구문을 사용하여 표시됩니다.
attr
(선택 사항)
decl-specifier-seq
declarator
=
initializer
|
(1) | ||||||||
attr
(선택 사항)
decl-specifier-seq
abstract-declarator
(선택 사항)
=
initializer
|
(2) | ||||||||
기본 인수는 함수 호출에서 누락된 후행 인수 대신 사용됩니다:
void point(int x = 3, int y = 4); point(1, 2); // point(1, 2)를 호출합니다 point(1); // point(1, 4)를 호출합니다 point(); // point(3, 4)를 호출합니다
함수 선언에서 기본 인수를 가진 매개변수 뒤에 오는 모든 후속 매개변수는 다음을 충족해야 합니다:
- 이 범위 또는 동일한 범위의 이전 선언에서 제공된 기본 인수를 가짐:
int x(int = 1, int); // 오류: 오직 후행 매개변수만 기본 인자를 가질 수 있음 // ("x"의 이전 선언이 없다고 가정할 때) void f(int n, int k = 1); void f(int n = 0, int k); // 정상: "k"의 기본 인자는 동일한 범위의 // 이전 선언에서 제공됨 void g(int, int = 7); void h() { void g(int = 1, int); // 오류: 동일한 범위가 아님 }
template<class... T> struct C { void f(int n = 0, T...); }; C<int> c; // OK; instantiates declaration void C::f(int n = 0, int)
template<class... T> void h(int i = 0, T... args); // OK |
(C++11부터) |
생략 부호는 매개변수가 아니므로 기본 인수가 있는 매개변수 뒤에 올 수 있습니다:
int g(int n = 0, ...); // 정상
기본 인수는 함수 선언 및 람다 표현식 의 매개변수 목록에서만 허용되며, (C++11부터) 함수 포인터, 함수 참조 선언이나 typedef 선언에서는 허용되지 않습니다. 템플릿 매개변수 목록은 기본 템플릿 인수 에 대해 유사한 구문을 사용합니다.
비템플릿 함수의 경우, 동일한 스코프에서 함수가 재선언되면 이미 선언된 함수에 기본 인자를 추가할 수 있습니다. 함수 호출 시점에서 기본 인자는 해당 함수에 대해 표시되는 모든 선언에서 제공된 기본 인자의 합집합입니다. 재선언은 이미 표시되는 기본 인자가 있는 매개변수에 대해 기본 인자를 도입할 수 없습니다(값이 동일한 경우에도). 내부 스코프에서의 재선언은 외부 스코프의 기본 인자를 상속받지 않습니다.
void f(int, int); // #1 void f(int, int = 7); // #2 OK: 기본 인수를 추가함 void h() { f(3); // #1과 #2가 스코프 내에 있음; f(3,7) 호출을 수행함 void f(int = 1, int); // 오류: 두 번째 매개변수의 기본 인수가 // 외부 스코프에서 상속되지 않음 } void m() { // 새로운 스코프 시작 void f(int, int); // 내부 스코프 선언; 기본 인수가 없음. f(4); // 오류: f(int, int)를 호출하기에 인수가 부족함 void f(int, int = 6); f(4); // OK: f(4, 6) 호출; void f(int, int = 6); // 오류: 두 번째 매개변수는 이미 // 기본 인수를 가지고 있음 (값이 동일하더라도) } void f(int = 1, int); // #3 OK, #2에 기본 인수를 추가함 void n() { // 새로운 스코프 시작 f(); // #1, #2, #3이 스코프 내에 있음: f(1, 7) 호출; }
만약 inline 함수가 다른 번역 단위에서 선언된다면, 기본 인자들의 누적 집합은 각 번역 단위의 끝에서 반드시 동일해야 합니다.
|
인라인이 아닌 함수가 다른 번역 단위에서 동일한 네임스페이스 범위로 선언된 경우, 해당 기본 인수(기본 인자가 존재한다면)는 동일해야 합니다(단, 일부 번역 단위에서는 일부 기본 인수가 없을 수 있습니다). |
(since C++20) |
만약 friend 선언이 기본 인수를 지정한다면, 이것은 friend 함수 정의여야 하며, 이 함수의 다른 선언은 번역 단위에서 허용되지 않습니다.
using 선언 은 알려진 기본 인수 집합을 이전하며, 나중에 함수의 네임스페이스에 더 많은 기본 인수가 추가되면, 해당 기본 인수들도 using 선언이 보이는 모든 곳에서 보입니다:
namespace N { void f(int, int = 1); } using N::f; void g() { f(7); // f(7, 1)를 호출합니다; f(); // 오류 } namespace N { void f(int = 2, int); } void h() { f(); // f(2, 1)를 호출합니다; }
기본 인수에 사용된 이름들은 선언 시점에서 조회되고, 접근성 을 확인하며 바인딩되지만, 함수 호출 시점에서 실행됩니다:
int a = 1; int f(int); int g(int x = f(a)); // f 탐색은 ::f를 찾고, a 탐색은 ::a를 찾음 // 이 시점에서 값이 1인 ::a의 값은 사용되지 않음 void h() { a = 2; // ::a의 값을 변경 { int a = 3; g(); // f(2)를 호출한 후, 그 결과로 g()를 호출 } }
비- 멤버 함수 가 아닌 템플릿화된 클래스의 멤버 함수의 경우, 기본 인수는 클래스 외부 정의에서 허용되며, 클래스 본문 내 선언에서 제공된 기본 인수와 결합됩니다. 만약 이러한 클래스 외부 기본 인수가 멤버 함수를 기본 생성자나 복사 /이동 (C++11부터) 생성자/대입 연산자로 변환하는 경우(호출을 모호하게 만듦), 프로그램은 형식에 맞지 않습니다. 템플릿화된 클래스의 멤버 함수의 경우, 모든 기본 인수는 멤버 함수의 최초 선언에서 제공되어야 합니다.
class C { void f(int i = 3); void g(int i, int j = 99); C(int arg); // 기본 생성자가 아닌 생성자 }; void C::f(int i = 3) {} // 오류: 기본 인수가 이미 // 클래스 범위에서 지정됨 void C::g(int i = 88, int j) {} // OK: 이 번역 단위에서, // C::g는 인수 없이 호출 가능 C::C(int arg = 1) {} // 오류: 이를 기본 생성자로 변환함
virtual 함수의 재정의자는 기본 클래스 선언에서 기본 인수를 상속받지 않으며, 가상 함수 호출이 발생할 때 기본 인수는 객체의 정적 타입을 기반으로 결정됩니다(참고: 이는 non-virtual interface 패턴으로 회피할 수 있습니다).
struct Base { virtual void f(int a = 7); }; struct Derived : Base { void f(int a) override; }; void m() { Derived d; Base& b = d; b.f(); // 정상: Derived::f(7) 호출 d.f(); // 오류: 기본 인자가 없음 }
기본 인수에서는 지역 변수가 허용되지 않습니다. 단, 평가되지 않는 경우 는 예외입니다:
void f() { int n = 1; extern void g(int x = n); // 오류: 지역 변수는 기본 인수로 사용할 수 없음 extern void h(int x = sizeof n); // CWG 2082부터 허용됨 }
this 포인터는 기본 인수에서 허용되지 않습니다:
class A { void f(A* p = this) {} // 오류: 허용되지 않음 };
비정적 클래스 멤버는 기본 인수에서 허용되지 않습니다(평가되지 않는 경우에도). 단, 멤버에 대한 포인터를 형성하거나 멤버 접근 표현식에서 사용되는 경우는 예외입니다:
int b; class X { int a; int mem1(int i = a); // 오류: 비정적 멤버를 사용할 수 없음 int mem2(int i = b); // OK: 조회 결과 X::b 정적 멤버를 찾음 int mem3(int X::* i = &X::a); // OK: 비정적 멤버를 사용할 수 있음 int mem4(int i = x.a); // OK: 멤버 접근 표현식 내에서 static X x; static int b; };
기본 인수는 해당 매개변수에 대한 인수 없이 함수가 호출될 때마다 평가됩니다. 함수 매개변수는 평가되지 않는 경우 를 제외하고는 기본 인수에서 허용되지 않습니다. 매개변수 목록에서 앞쪽에 나타나는 매개변수들이 scope 에 있음에 유의하십시오:
int a; int f(int a, int b = a); // 오류: 기본 인수에서 사용된 매개변수 a int g(int a, int b = sizeof a); // CWG 2082 해결 전까지 오류 // 해결 후 정상: 평가되지 않은 컨텍스트에서의 사용은 허용됨
기본 인수는 함수 타입의 일부가 아닙니다:
int f(int = 0); void h() { int j = f(1); int k = f(); // f(0) 호출; } int (*p1)(int) = &f; int (*p2)() = &f; // 오류: f의 타입은 int(int)
함수 호출 연산자 와 첨자 연산자 (C++23부터) 를 제외한 다른 연산자 함수들은 기본 인수를 가질 수 없습니다:
class C { int operator++(int i = 0); // 잘못된 형식 int operator[](int j = 0); // C++23부터 허용됨 int operator()(int k = 0); // 허용됨 };
|
명시적 객체 매개변수 는 기본 인수를 가질 수 없습니다: struct S { void f(this const S& = S{}); }; // ill-formed |
(C++23부터) |
참고
매개변수 이름이 없는 경우 복합 할당 토큰을 피하기 위해 공백이 필요할 수 있습니다( maximal munch 참조).
void f1(int*=0); // 오류: 여기서 "*="는 예상치 못한 토큰입니다 void g1(const int&=0); // 오류: 여기서 "&="는 예상치 못한 토큰입니다 void f2(int* = 0); // 정상 void g2(const int& = 0); // 정상 void h(int&&=0); // 공백 없어도 정상, "&&"는 여기서 토큰입니다
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 217 | C++98 |
클래스 템플릿의 비템플릿 멤버 함수에
기본 인자를 추가할 수 있었음 |
금지됨 |
| CWG 1344 | C++98 |
멤버 함수의 클래스 외부 정의에서 추가된 기본 인자가
특수 멤버 함수로 변경될 수 있었음 |
금지됨 |
| CWG 1716 | C++98 |
기본 인자는 호출자가 인자를 제공한 경우에도
함수가 호출될 때마다 평가됨 |
해당 매개변수에 대한 인자가
제공되지 않은 경우에만 평가됨 |
| CWG 2082 | C++98 |
기본 인자가 비평가 문맥에서 지역 변수와
선행 매개변수를 사용하는 것이 금지됨 |
비평가 문맥에서의
사용이 허용됨 |
| CWG 2233 | C++11 |
매개변수 팩에서 확장된 매개변수가 기본 인자가 있는
매개변수 뒤에 나타날 수 없었음 |
허용됨 |
| CWG 2683 | C++98 |
클래스 템플릿의 중첩 클래스 멤버 함수의
클래스 외부 정의에 기본 인자가 있을 수 있었음 |
금지됨 |