Function declaration
함수 선언은 함수 이름과 그 타입을 도입합니다. 함수 정의는 함수 이름/타입을 함수 본문과 연결합니다.
목차 |
함수 선언
함수 선언은 어떤 범위에서든 나타날 수 있습니다. 클래스 범위에서의 함수 선언은 ( friend 지정자가 사용되지 않는 한) 클래스 멤버 함수를 도입합니다. 자세한 내용은 member functions 와 friend functions 를 참조하십시오.
noptr-declarator
(
parameter-list
)
cv
(선택 사항)
ref
(선택 사항)
except
(선택 사항)
attr
(선택 사항)
|
(1) | ||||||||
noptr-declarator
(
parameter-list
)
cv
(선택 사항)
ref
(선택 사항)
except
(선택 사항)
attr
(선택 사항)
->
trailing
|
(2) | (C++11 이후) | |||||||
(다른 형태의 declarator 구문에 대해서는 Declarations 참조)
| noptr-declarator | - |
유효한
declarator
이지만,
*
,
&
, 또는
&&
로 시작하는 경우 괄호로 둘러싸여야 함
|
||||||
| parameter-list | - | 함수 매개변수의 쉼표로 구분된 목록 (비어 있을 수 있음, 자세한 내용은 아래 참조) | ||||||
| attr | - | (C++11부터) attributes 목록. 이러한 속성은 함수 자체가 아닌 함수의 타입에 적용됩니다. 함수에 대한 속성은 선언자 내 식별자 뒤에 나타나며, 선언 시작 부분에 나타나는 속성(있는 경우)과 결합됩니다. | ||||||
| cv | - | const/volatile 한정자, 비정적 멤버 함수 선언에서만 허용됨 | ||||||
| ref | - | (C++11부터) ref-qualification, 비정적 멤버 함수 선언에서만 허용됨 | ||||||
| except | - |
|
||||||
| trailing | - | 후행 반환 타입, 반환 타입이 인수 이름에 의존하는 경우(예: template < class T, class U > auto add ( T t, U u ) - > decltype ( t + u ) ; ) 또는 복잡한 경우(예: auto fpif ( int ) - > int ( * ) ( int ) )에 유용함 |
|
선언 에서 언급된 바와 같이, 선언자 뒤에는 requires 절 이 올 수 있으며, 이는 함수의 연관된 제약 조건 을 선언합니다. 이 제약 조건은 오버로드 해결 에 의해 함수가 선택되기 위해 충족되어야 합니다. (예시: void f1 ( int a ) requires true ; ) 연관된 제약 조건은 함수 시그니처의 일부이지만, 함수 타입의 일부는 아닙니다. |
(C++20부터) |
함수 선언자는 다른 선언자들과 혼합될 수 있으며, 여기서 선언 지정자 시퀀스 가 허용하는 경우:
// int, int*, 함수, 함수 포인터를 선언합니다 int a = 1, *p = NULL, f(), (*pf)(double); // decl-specifier-seq는 int입니다 // 선언자 f()는 인자를 받지 않고 int를 반환하는 함수를 // 선언(정의는 아님)합니다 struct S { virtual int f(char) const, g(int) &&; // 두 개의 비정적 멤버 함수를 선언합니다 virtual int f(char), x; // 컴파일 오류: virtual (decl-specifier-seq 내) // 비정적 멤버 함수 선언에서만 허용됩니다 // };
|
volatile 한정 객체 타입을 매개변수 타입이나 반환 타입으로 사용하는 것은 deprecated되었습니다. |
(since C++20) |
함수의 반환 타입은 함수 타입이나 배열 타입이 될 수 없습니다(하지만 이들에 대한 포인터나 참조는 가능합니다).
|
다른 선언과 마찬가지로, 선언 앞에 나타나는 속성과 선언자 내 식별자 바로 뒤에 나타나는 속성 모두 선언되거나 정의되는 개체(이 경우 함수)에 적용됩니다: [[noreturn]] void f [[noreturn]] (); // OK: 두 속성 모두 함수 f에 적용됨 그러나 선언자 뒤에 나타나는 속성(위 구문에서)은 함수 자체가 아닌 함수의 타입에 적용됩니다: void f() [[noreturn]]; // 오류: 이 속성은 함수 자체에 아무런 영향을 미치지 않음 |
(C++11부터) |
반환 타입 추론함수 선언의 decl-specifier-seq 에 auto 키워드가 포함된 경우, 후행 반환 타입을 생략할 수 있으며 컴파일러가 폐기되지 않은 return 문에서 사용된 피연산자의 타입으로부터 추론합니다. 반환 타입이 decltype ( auto ) 를 사용하지 않는 경우, 추론은 템플릿 인수 추론 의 규칙을 따릅니다: int x = 1; auto f() { return x; } // 반환 타입은 int const auto& f() { return x; } // 반환 타입은 const int&
반환 타입이
decltype
(
auto
)
인 경우, 반환 타입은 반환문에서 사용된 피연산자가
int x = 1; decltype(auto) f() { return x; } // 반환 타입은 int, decltype(x)와 동일 decltype(auto) f() { return(x); } // 반환 타입은 int&, decltype((x))와 동일 (참고: “ const decltype ( auto ) & ”는 오류입니다. decltype ( auto ) 는 반드시 단독으로 사용되어야 합니다) 여러 개의 return 문이 있는 경우, 모두 동일한 타입으로 추론되어야 합니다: auto f(bool val) { if (val) return 123; // 반환 타입 int로 추론됨 else return 3.14f; // 오류: 반환 타입 float로 추론됨 } 반환문이 없거나 반환문의 피연산자가 void 표현식인 경우(피연산자가 없는 반환문 포함), 선언된 반환 타입은 반드시 다음 중 하나여야 합니다: decltype ( auto ) , 이 경우 추론된 반환 타입은 void 입니다, 또는 (possibly cv-qualified) auto , 이 경우 추론된 반환 타입은 (동일하게 cv-qualified) void 가 됩니다: auto f() {} // void를 반환 auto g() { return f(); } // void를 반환 auto* x() {} // 오류: void에서 auto*를 추론할 수 없음 함수에서 return 문이 한 번 확인되면, 해당 문에서 추론된 반환 타입은 함수의 나머지 부분, 다른 return 문을 포함하여 사용될 수 있습니다: auto sum(int i) { if (i == 1) return i; // sum의 반환 타입은 int else return sum(i - 1) + i; // OK: sum의 반환 타입은 이미 알려져 있음 } 반환문이 중괄호로 둘러싸인 초기화 리스트 를 사용하는 경우, 추론은 허용되지 않습니다: auto func() { return {1, 2, 3}; } // 오류 가상 함수 와 코루틴 (C++20부터) 은 반환형 추론을 사용할 수 없습니다: struct F { virtual auto f() { return 2; } // 오류 }; 함수 템플릿 중 사용자 정의 변환 함수 를 제외한 다른 함수 템플릿들은 반환형 추론을 사용할 수 있습니다. 반환문의 표현식이 의존적 이지 않더라도 추론은 인스턴스화 시점에 발생합니다. 이 인스턴스화는 SFINAE 의 목적상 즉시 문맥에 속하지 않습니다. template<class T> auto f(T t) { return t; } typedef decltype(f(1)) fint_t; // f<int>를 인스턴스화하여 반환 타입을 추론함 template<class T> auto f(T* t) { return *t; } void g() { int (*p)(int*) = &f; } // 두 f를 모두 인스턴스화하여 반환 타입을 결정하고, // 두 번째 템플릿 오버로드를 선택함 반환형 추론을 사용하는 함수 또는 함수 템플릿의 재선언 또는 특수화는 동일한 반환형 자리 표시자를 사용해야 합니다: auto f(int num) { return num; } // int f(int num); // 오류: 자리 표시자 반환 타입 없음 // decltype(auto) f(int num); // 오류: 다른 자리 표시자 template<typename T> auto g(T t) { return t; } template auto g(int); // OK: 반환 타입은 int // template char g(char); // 오류: 기본 템플릿 g의 특수화가 아님 마찬가지로, 반환형 추론을 사용하지 않는 함수나 함수 템플릿의 재선언이나 특수화는 플레이스홀더를 사용해서는 안 됩니다: int f(int num); // auto f(int num) { return num; } // 오류: f의 재선언이 아님 template<typename T> T g(T t) { return t; } template int g(int); // OK: T를 int로 특수화 // template auto g(char); // 오류: 기본 템플릿 g의 특수화가 아님 명시적 인스턴스화 선언 은 반환 타입 추론을 사용하는 함수 템플릿을 자체적으로 인스턴스화하지 않습니다: template<typename T> auto f(T t) { return t; } extern template auto f(int); // f<int>를 인스턴스화하지 않음 int (*p)(int) = f; // 반환 타입을 결정하기 위해 f<int>를 인스턴스화하지만, // 명시적 인스턴스화 정의는 여전히 프로그램 어딘가에 필요함 |
(C++14 이후) |
매개변수 목록
매개변수 목록은 함수가 호출될 때 지정할 수 있는 인수를 결정합니다. 이는 쉼표로 구분된 매개변수 선언 들의 목록으로, 각각은 다음과 같은 구문을 가집니다:
| attr (선택 사항) decl-specifier-seq declarator | (1) | ||||||||
|
attr
(선택 사항)
|
(2) | (C++23부터) | |||||||
attr
(선택 사항)
decl-specifier-seq
declarator
=
initializer
|
(3) | ||||||||
| attr (선택 사항) decl-specifier-seq abstract-declarator (선택 사항) | (4) | ||||||||
|
attr
(선택 사항)
|
(5) | (C++23부터) | |||||||
attr
(선택 사항)
decl-specifier-seq
abstract-declarator
(선택 사항)
=
initializer
|
(6) | ||||||||
void
|
(7) | ||||||||
| 잘못된 사용법 | 예시 |
|---|---|
| 여러 매개변수가 존재하는 경우 | int f1 ( void , int ) ; |
| void 매개변수가 이름을 가지는 경우 | inf f2 ( void param ) ; |
| void 가 cv-한정된 경우 | int f3 ( const void ) ; |
| void 가 종속 이름 인 경우 |
int
f4
(
T
)
;
(여기서
T
가
void
인 경우)
|
| void 매개변수가 명시적 객체 매개변수 인 경우 (C++23부터) | int f5 ( this void ) ; |
|
decl-specifier-seq 가 specifiers 중 타입 지정자 외의 다른 지정자들이 존재할 수 있음을 암시하지만, 허용되는 다른 지정자는 register 와 auto (C++11까지) 뿐이며, 이는 아무 효과도 없습니다. |
(C++17까지) |
|
함수 매개변수 중 하나라도 placeholder ( auto 또는 concept type )를 사용하면, 해당 함수 선언은 abbreviated function template 선언이 됩니다: void f1(auto); // same as template<class T> void f1(T) void f2(C1 auto); // same as template<C1 T> void f2(T), if C1 is a concept |
(C++20부터) |
|
this 지정자를 가진 매개변수 선언( ( 2 ) / ( 5 ) 구문)은 explicit object parameter 를 선언합니다. explicit object parameter는 function parameter pack 이 될 수 없으며, 다음 선언들의 매개변수 목록에서 첫 번째 매개변수로만 나타날 수 있습니다:
explicit object parameter를 가진 멤버 함수에는 다음과 같은 제한이 있습니다: struct C { void f(this C& self); // OK template<typename Self> void g(this Self&& self); // also OK for templates void p(this C) const; // Error: “const” not allowed here static void q(this C); // Error: “static” not allowed here void r(int, this C); // Error: an explicit object parameter // can only be the first parameter }; // void func(this C& self); // Error: non-member functions cannot have // an explicit object parameter |
(C++23부터) |
함수 선언에서 선언된 매개변수 이름은 일반적으로 자체 문서화 목적으로만 사용됩니다. 함수 정의에서는 사용되지만(그러나 여전히 선택적입니다).
매개변수 목록에서 타입 이름이 괄호 안에 중첩될 때 모호성이 발생합니다 ( lambda expressions 포함) (C++11부터) . 이 경우, 함수 포인터 타입의 매개변수 선언과 declarator 의 식별자를 둘러싼 중복 괄호가 있는 매개변수 선언 사이에서 선택해야 합니다. 해결 방법은 타입 이름을 simple type specifier (함수 포인터 타입)로 간주하는 것입니다:
class C {}; void f(int(C)) {} // void f(int(*fp)(C param)) {} // NOT void f(int C) {} void g(int *(C[10])); // void g(int *(*fp)(C param[10])); // NOT void g(int *C[10]);
매개변수 타입은 알려지지 않은 경계의 배열에 대한 참조나 포인터를 포함하는 타입일 수 없으며, 여기에는 다단계 포인터/배열이나 이러한 타입을 매개변수로 하는 함수 포인터도 포함됩니다.
생략 부호 사용하기
매개변수 목록의 마지막 매개변수는 생략 부호( ... )일 수 있습니다; 이것은 가변 인자 함수 를 선언합니다. 생략 부호 앞의 쉼표는 생략할 수 있습니다 (C++26에서 사용 중단됨) :
int printf(const char* fmt, ...); // 가변 인수 함수 int printf(const char* fmt...); // 위와 동일하지만 C++26부터 사용 중단됨 template<typename... Args> void f(Args..., ...); // 매개변수 팩을 가진 가변 함수 템플릿 template<typename... Args> void f(Args... ...); // 위와 동일하지만 C++26부터 사용 중단됨 template<typename... Args> void f(Args......); // 위와 동일하지만 C++26부터 사용 중단됨
함수 타입
매개변수-타입-목록
함수의 parameter-type-list 는 다음과 같이 결정됩니다:
- 각 매개변수의 타입 (함수 parameter packs 포함) (since C++11) 은 자체 parameter declaration 에서 결정됩니다.
-
각 매개변수의 타입을 결정한 후, "
T의 배열" 타입이나 함수 타입T인 매개변수는 "T에 대한 포인터"로 조정됩니다. - 매개변수 타입 목록을 생성한 후, 함수 타입을 형성할 때 매개변수 타입을 수정하는 모든 최상위 cv-qualifiers 가 삭제됩니다.
- 변환된 매개변수 타입들의 결과 목록과 ellipsis 또는 함수 parameter pack (since C++11) 의 존재 여부가 함수의 parameter-type-list입니다.
void f(char*); // #1 void f(char[]) {} // #1을 정의함 void f(const char*) {} // OK, 다른 오버로드 void f(char* const) {} // 오류: #1을 재정의함 void g(char(*)[2]); // #2 void g(char[3][2]) {} // #2를 정의함 void g(char[3][3]) {} // OK, 다른 오버로드 void h(int x(const int)); // #3 void h(int (*)(int)) {} // #3을 정의함
함수 타입 결정
구문
(1)
에서,
noptr-declarator
를 독립적인 선언으로 가정할 때,
noptr-declarator
내의
qualified-id
또는
unqualified-id
의 타입을 "derived-declarator-type-list
T
"로 지정합니다:
|
(C++17부터) |
-
다음의 경우
(C++17까지)
그렇지 않은 경우,
(C++17부터)
선언된 함수의 타입은
"derived-declarator-type-list 함수로서
parameter-type-list cv (선택적) ref (선택적) (C++11부터) 를 가지며T를 반환하는" 타입입니다.
|
구문
(2)
에서,
noptr-declarator
를 독립적인 선언으로 가정할 때,
noptr-declarator
내의
qualified-id
또는
unqualified-id
의 타입이 "derived-declarator-type-list
|
(C++11부터) |
|
(C++17부터) |
attr 이 있는 경우, 함수 타입에 적용됩니다. |
(C++11부터) |
// "f1"의 타입은 // "noreturn 속성을 가진 int를 받고 void를 반환하는 함수"입니다 void f1(int a) [[noreturn]]; // "f2"의 타입은 // "constexpr noexcept int 포인터를 받고 int를 반환하는 함수"입니다 constexpr auto f2(int[] b) noexcept -> int; struct X { // "f3"의 타입은 // "매개변수 없이 const로 const int를 반환하는 함수"입니다 const int f3() const; };
후행 한정자
함수 타입에
cv
또는
ref
(C++11부터)
(
typedef
이름으로 명명된 타입 포함)가 있는 경우 다음으로만 나타날 수 있습니다:
- 비정적 멤버 함수 의 함수 유형,
- 멤버 포인터가 참조하는 함수 유형,
- 함수 typedef 선언 또는 별칭 선언 (C++11부터) 의 최상위 함수 유형,
- 템플릿 타입 매개변수 의 기본 인수 내 타입 ID , 또는
- 템플릿 타입 매개변수에 대한 템플릿 인수의 타입 ID.
typedef int FIC(int) const; FIC f; // 오류: 멤버 함수를 선언하지 않음 struct S { FIC f; // 정상 }; FIC S::*pm = &S::f; // 정상
함수 시그니처
모든 함수에는 시그니처가 있습니다.
함수의 시그니처는 함수 이름과 parameter-type-list 로 구성됩니다. 또한 다음 예외를 제외하고는 포함하는 namespace 도 시그니처에 포함됩니다:
- 함수가 member function 인 경우, 해당 함수의 시그니처는 포함된 네임스페이스 대신 함수가 멤버로 속한 클래스를 포함합니다. 또한 시그니처는 다음과 같은 구성 요소가 존재할 경우 포함합니다:
-
- cv
|
(C++11부터) |
|
(C++20부터) |
except and attr (since C++11) 는 함수 시그니처에 포함되지 않습니다 , 비록 noexcept specification 이 함수 타입에 영향을 미치더라도 (since C++17) .
함수 정의
비멤버 함수 정의는 네임스페이스 범위에서만 나타날 수 있습니다 (중첩 함수는 존재하지 않습니다). 멤버 함수 정의는 또한 클래스 정의 본문 안에 나타날 수 있습니다. 이들은 다음과 같은 구문을 가집니다:
|
attr
(선택 사항)
decl-specifier-seq
(선택 사항)
declarator
virt-specs (선택 사항) contract-specs (선택 사항) function-body |
(1) | ||||||||
|
attr
(선택 사항)
decl-specifier-seq
(선택 사항)
declarator
requires-clause contract-specs (선택 사항) function-body |
(2) | (C++20 이후) | |||||||
| attr | - | (since C++11) 속성 목록. 이 속성들은 식별자 뒤의 선언자(이 페이지 상단 참조)에 있는 속성들과 결합됩니다(있는 경우). |
| decl-specifier-seq | - | 선언 문법 에서와 같이 지정자를 포함한 반환 타입 |
| declarator | - | 함수 선언자, 위의 함수 선언 문법과 동일(괄호로 묶을 수 있음) |
| virt-specs | - |
(since C++11)
override
,
final
, 또는 이들의 임의 순서 조합
|
| requires-clause | - | requires 절 |
| contract-specs | - | (since C++26) 함수 계약 지정자 목록 |
| function-body | - | 함수 본문(아래 참조) |
function-body
는 다음 중 하나입니다:
| ctor-initializer (선택 사항) compound-statement | (1) | ||||||||
| function-try-block | (2) | ||||||||
=
default
;
|
(3) | (C++11부터) | |||||||
=
delete
;
|
(4) | (C++11부터) | |||||||
=
delete
(
string-literal
);
|
(5) | (C++26부터) | |||||||
| ctor-initializer | - | member initializer list , 생성자에서만 허용됨 |
| compound-statement | - | 함수의 본체를 구성하는 중괄호로 둘러싸인 문장 시퀀스 |
| function-try-block | - | 함수 try 블록 |
| string-literal | - | 함수가 삭제된 이유를 설명하는 데 사용될 수 있는 평가되지 않은 문자열 리터럴 |
int max(int a, int b, int c) { int m = (a > b) ? a : b; return (m > c) ? m : c; } // 선언 지정자 시퀀스는 "int"입니다 // 선언자는 "max(int a, int b, int c)"입니다 // 본문은 { ... }입니다
함수 본문은 compound statement (한 쌍의 중괄호로 둘러싸인 0개 이상의 statements 시퀀스)이며, 함수 호출 시 실행됩니다. 또한 constructor 의 함수 본문에는 다음도 포함됩니다:
- 생성자의 멤버 초기화 목록 에 식별자가 없는 모든 비정적 데이터 멤버에 대해, 기본 멤버 초기화자 또는 (C++11부터) 기본 초기화 가 해당 멤버 하위 객체 를 초기화하는 데 사용됩니다.
- 생성자의 멤버 초기화 목록에 타입 이름이 없는 모든 기본 클래스에 대해, 기본 초기화가 해당 기본 클래스 하위 객체를 초기화하는 데 사용됩니다.
|
함수 정의에 virt-specs 가 포함된 경우, 이는 반드시 멤버 함수 를 정의해야 합니다. |
(since C++11) |
|
함수 정의에 requires-clause 가 포함된 경우, 이는 반드시 템플릿 함수 를 정의해야 합니다. |
(since C++20) |
void f() override {} // 오류: 멤버 함수가 아님 void g() requires (sizeof(int) == 4) {} // 오류: 템플릿 함수가 아님
함수 정의의 매개변수 타입과 반환 타입은 (cv 한정자가 있을 수 있는) incomplete class types 일 수 없습니다. 단, 함수가 삭제된 것으로 정의된 경우는 예외입니다 (C++11부터) . 완전성 검사는 함수 본문에서만 수행되며, 이로 인해 member functions 가 정의된 지점에서 불완전하더라도 (함수 본문에서는 완전한) 자신이 정의된 클래스나 이를 포함하는 클래스를 반환할 수 있습니다.
함수 정의의 선언자 에서 선언된 매개변수들은 본문 내에서 범위를 가집니다. 만약 매개변수가 함수 본문에서 사용되지 않으면, 이름을 지정할 필요가 없습니다(추상 선언자를 사용하는 것으로 충분합니다):
void print(int a, int) // 두 번째 매개변수는 사용되지 않음 { std::printf("a = %d\n", a); }
매개변수의 최상위 cv-qualifier 들이 함수 선언에서 무시되더라도, 이들은 함수 본문 내에서 보이는 매개변수의 타입을 수정합니다:
void f(const int n) // void(int) 타입의 함수를 선언함 { // 하지만 본문 내에서 "n"의 타입은 const int임 }
디폴트 함수함수 정의가 구문 ( 3 ) 인 경우, 해당 함수는 명시적으로 기본 정의됨 으로 정의됩니다. 명시적으로 기본 설정된 함수는 특수 멤버 함수 또는 비교 연산자 함수 (C++20부터) 여야 하며, 기본 인자 를 가져서는 안 됩니다.
명시적으로 기본 설정된 특수 멤버 함수
첫 번째 선언에서 명시적으로 기본값으로 설정된 함수는 암시적으로 inline 이며, 가능한 경우 암시적으로 constexpr function 입니다. struct S { S(int a = 0) = default; // 오류: 기본 인수 void operator=(const S&) = default; // 오류: 일치하지 않는 반환 타입 ~S() noexcept(false) = default; // OK, 다른 예외 명세 private: int i; S(S&); // OK, private 복사 생성자 }; S::S(S&) = default; // OK, 복사 생성자 정의 명시적으로 기본 설정된 함수와 암시적으로 선언된 함수를 통칭하여 기본 설정된 함수라고 합니다. 이들의 실제 정의는 암시적으로 제공되며, 자세한 내용은 해당 페이지를 참조하십시오. 삭제된 함수함수 정의가 구문 ( 4 ) 또는 ( 5 ) (C++26부터) 인 경우, 해당 함수는 명시적으로 삭제된(explicitly deleted) 것으로 정의됩니다. 삭제된 함수의 모든 사용은 잘못된 형식입니다(프로그램이 컴파일되지 않습니다). 여기에는 명시적 호출(함수 호출 연산자 사용)과 암시적 호출(삭제된 오버로딩된 연산자, 특수 멤버 함수, 할당 함수 등에 대한 호출), 삭제된 함수에 대한 포인터 또는 멤버 포인터 생성, 그리고 잠재적으로 평가될 수 있는 표현식 이 아닌 표현식에서 삭제된 함수의 사용까지 포함됩니다. 순수하지 않은 가상 멤버 함수는 암묵적으로 odr-used 되더라도 삭제된(deleted) 함수로 정의될 수 있습니다. 삭제된 함수는 삭제된 함수로만 오버라이드할 수 있으며, 삭제되지 않은 함수는 삭제되지 않은 함수로만 오버라이드할 수 있습니다.
함수가 오버로드된 경우, 오버로드 해결 이 먼저 수행되며, 삭제된 함수가 선택된 경우에만 프로그램이 잘못된 형식입니다: struct T { void* operator new(std::size_t) = delete; void* operator new[](std::size_t) = delete("new[] is deleted"); // C++26부터 }; T* p = new T; // 오류: 삭제된 T::operator new 호출 시도 T* p = new T[5]; // 오류: 삭제된 T::operator new[] 호출 시도, // "new[] is deleted" 진단 메시지 출력 삭제된 함수의 정의는 반드시 번역 단위에서 첫 번째 선언이어야 합니다: 이전에 선언된 함수를 삭제된 함수로 재선언할 수 없습니다: struct T { T(); }; T::T() = delete; // 오류: 첫 번째 선언에서 삭제되어야 함 사용자 제공 함수함수는 첫 번째 선언에서 명시적으로 기본 설정되거나 삭제되지 않은 사용자 선언 함수인 경우 사용자 제공(user-provided) 입니다. 사용자 제공 명시적 기본 함수(즉, 첫 번째 선언 이후 명시적으로 기본 설정된 함수)는 명시적으로 기본 설정된 지점에서 정의됩니다; 이러한 함수가 암시적으로 삭제된 것으로 정의되면 프로그램은 ill-formed입니다. 첫 번째 선언 이후 함수를 기본 설정으로 선언하면 진화하는 코드 베이스에 안정적인 바이너리 인터페이스를 제공하면서도 효율적인 실행과 간결한 정의를 제공할 수 있습니다. // "trivial"의 모든 특수 멤버 함수들은 // 각각 첫 번째 선언에서 기본값으로 설정되었으며, // 사용자 제공(user-provided)이 아닙니다 struct trivial { trivial() = default; trivial(const trivial&) = default; trivial(trivial&&) = default; trivial& operator=(const trivial&) = default; trivial& operator=(trivial&&) = default; ~trivial() = default; }; struct nontrivial { nontrivial(); // 첫 번째 선언 }; // 첫 번째 선언에서 기본값으로 설정되지 않았으며, // 사용자 제공(user-provided)이며 여기서 정의됩니다 nontrivial::nontrivial() = default; 모호성 해결
함수 본문과
initializer
가
using T = void(); // 함수 타입 using U = int; // 비-함수 타입 T a{}; // 아무 작업도 하지 않는 함수를 정의 U b{}; // int 객체를 값 초기화 T c = delete("hello"); // 함수를 삭제된 것으로 정의 U d = delete("hello"); // int 객체를 delete 표현식의 결과로 // 복사 초기화 (잘못된 형식)
__func__함수 본문 내에서, 함수-지역 사전 정의 변수 __func__ 는 다음과 같이 정의된 것처럼 정의됩니다 static const char __func__[] = "function-name"; 이 변수는 블록 범위와 정적 저장 기간을 가집니다: struct S { S(): s(__func__) {} // OK: 초기화 목록은 함수 본문의 일부입니다 const char* s; }; void f(const char* s = __func__); // 오류: 매개변수 목록은 선언자의 일부입니다 |
(C++11 이후) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
함수 계약 지정자함수 선언과 람다 표현식 은 일련의 함수 계약 지정자 를 포함할 수 있으며, 각 지정자는 다음 구문을 가집니다:
1)
사전 조건 어서션(precondition assertion)
을 도입합니다.
2,3)
사후 조건 단언문(postcondition assertion)을 도입합니다.
2)
단언문이 결과에 바인딩되지 않습니다.
3)
단언문이 결과에 바인딩됩니다.
함수 계약 단언은 계약 단언 이 함수와 연관된 것입니다. 함수 계약 단언의 술어는 그 predicate contextually converted to bool 입니다. 다음 함수들은 함수 계약 지정자와 함께 선언될 수 없습니다: 사전 조건 어서션사전 조건 어설션은 함수 진입과 연관되어 있습니다: int divide(int dividend, int divisor) pre(divisor != 0) { return dividend / divisor; } double square_root(double num) pre(num >= 0) { return std::sqrt(num); } 사후 조건 단언문사후 조건 단언은 함수가 정상적으로 종료될 때와 연관됩니다. 사후 조건 단언문에 identifier 가 있는 경우, 함수 계약 지정자는 identifier 를 해당 함수의 결과 바인딩(result binding) 이름으로 도입합니다. 결과 바인딩은 해당 함수 호출로 반환된 객체나 참조를 나타냅니다. 결과 바인딩의 타입은 해당 함수의 반환 타입입니다. int absolute_value(int num) post(r : r >= 0) { return std::abs(num); } double sine(double num) post(r : r >= -1.0 && r <= 1.0) { if (std::isnan(num) || std::isinf(num)) // 예외를 통한 종료는 계약 위반을 야기하지 않음 throw std::invalid_argument("잘못된 인수"); return std::sin(num); } 사후 조건 단언문에 식별자 가 있고, 관련 함수의 반환 타입이 (cv 한정자가 있을 수 있는) void 인 경우, 프로그램은 잘못된 형식입니다: void f() post(r : r > 0); // 오류: "r"에 바인딩할 수 있는 값이 없음 비템플릿 함수의 선언된 반환 타입에 placeholder type 이 포함된 경우, identifier 를 가진 사후 조건 어설션은 함수 정의에서만 나타날 수 있습니다: auto g(auto&) post(r : r >= 0); // OK, "g"는 템플릿입니다 auto h() post(r : r >= 0); // 오류: 반환값을 명명할 수 없음 auto k() post(r : r >= 0) // OK, "k"는 정의입니다 { return 0; } 계약 일관성
함수 또는 함수 템플릿
func
의
재선언
선언
두 개의 contract-specs 는 동일한 순서로 동일한 함수 계약 지정자로 구성된 경우 동일합니다.
함수 계약 지정자
이 조건이 오직 두 개의 람다 표현식 비교로 인해 충족되지 않고, 이 람다 표현식들이 predicate s 내에 포함된 경우, 진단이 요구되지 않습니다. bool b1, b2; void f() pre (b1) pre([]{ return b2; }()); void f(); // OK, 함수 계약 지정자가 생략됨 void f() pre (b1) pre([]{ return b2; }()); // 오류: 클로저의 타입이 다름 void f() pre (b1); // 오류: 함수 계약 지정자가 다름 int g() post(r : b1); int g() post(b1); // 오류: 결과 바인딩이 없음 namespace N { void h() pre (b1); bool b1; void h() pre (b1); // 오류: 함수 계약 지정자가 일치하지 않음 // (일 정의 규칙에 따라) } |
(C++26부터) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
참고 사항
직접 초기화 구문을 사용한 변수 선언과 함수 선언 사이에 모호함이 있는 경우, 컴파일러는 항상 함수 선언을 선택합니다; 자세한 내용은 direct-initialization 을 참조하십시오.
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_decltype_auto
|
201304L
|
(C++14) |
decltype(auto)
|
__cpp_return_type_deduction
|
201304L
|
(C++14) | 반환형 추론 일반 함수용 |
__cpp_explicit_this_parameter
|
202110L
|
(C++23) | 명시적 객체 매개변수 ( this 추론 ) |
__cpp_deleted_function
|
202403L
|
(C++26) | 이유가 명시된 삭제된 함수 |
키워드
예제
#include <iostream> #include <string> // 기본 인자를 가진 간단한 함수, 아무것도 반환하지 않음 void f0(const std::string& arg = "world!") { std::cout << "Hello, " << arg << '\n'; } // 선언은 네임스페이스(파일) 범위에 있음 // (정의는 나중에 제공됨) int f1(); // f0을 가리키는 포인터를 반환하는 함수, C++11 이전 스타일 void (*fp03())(const std::string&) { return f0; } // f0을 가리키는 포인터를 반환하는 함수, C++11 후행 반환 타입 사용 auto fp11() -> void(*)(const std::string&) { return f0; } int main() { f0(); fp03()("test!"); fp11()("again!"); int f2(std::string) noexcept; // 함수 범위 내 선언 std::cout << "f2(\"bad\"): " << f2("bad") << '\n'; std::cout << "f2(\"42\"): " << f2("42") << '\n'; } // int를 반환하는 간단한 비멤버 함수 int f1() { return 007; } // 예외 명세와 함수 try 블록을 가진 함수 int f2(std::string str) noexcept try { return std::stoi(str); } catch (const std::exception& e) { std::cerr << "stoi() failed!\n"; return 0; } // 삭제된 함수, 호출 시도 시 컴파일 오류 발생 void bar() = delete # if __cpp_deleted_function ("reason") # endif ;
가능한 출력:
stoi() failed!
Hello, world!
Hello, test!
Hello, again!
f2("bad"): 0
f2("42"): 42
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 135 | C++98 |
클래스 내에서 정의된 멤버 함수는
자신의 클래스 타입을 매개변수나 반환 타입으로 가질 수 없음 (불완전 타입이므로) |
허용됨 |
| CWG 332 | C++98 | 매개변수가 cv-qualified void 타입을 가질 수 있음 | 금지됨 |
| CWG 393 | C++98 |
알려지지 않은 경계의 배열에 대한 포인터/참조를
포함하는 타입들은 매개변수가 될 수 없음 |
이러한 타입들 허용됨 |
| CWG 452 | C++98 | 멤버 초기화 목록이 함수 본문의 일부가 아님 | 함수 본문의 일부임 |
| CWG 577 | C++98 |
종속 타입
void
를 사용하여
매개변수 없는 함수를 선언할 수 있음 |
비종속
void 만 허용됨 |
| CWG 1327 | C++11 |
defaulted 또는 deleted 함수는
override 또는 final 로 지정할 수 없음 |
허용됨 |
| CWG 1355 | C++11 | 특별 멤버 함수만 사용자 제공이 가능했음 | 모든 함수로 확장됨 |
| CWG 1394 | C++11 |
deleted 함수는 불완전 타입의 매개변수를
가지거나 불완전 타입을 반환할 수 없음 |
불완전 타입 허용됨 |
| CWG 1824 | C++98 |
함수 정의의 매개변수 타입과 반환 타입에 대한
완전성 검사가 함수 정의의 문맥 외부에서 수행될 수 있음 |
함수 정의의
문맥에서만 검사 수행 |
| CWG 1877 | C++14 | 반환 타입 추론이 return ; 를 return void ( ) ; 로 처리함 |
이 경우 단순히 반환
타입을 void 로 추론함 |
| CWG 2015 | C++11 |
삭제된 가상 함수의 암시적 odr-use가
잘못된 형식임 |
이러한 odr-use는
사용 금지에서 제외됨 |
| CWG 2044 | C++14 |
void
를 반환하는 함수에 대한
반환 타입 추론이 선언된 반환 타입이 decltype ( auto ) 일 때 실패함 |
이 경우를 처리하도록
추론 규칙 갱신됨 |
| CWG 2081 | C++14 |
함수 재선언이 초기 선언에서 사용하지 않는
반환 타입 추론을 사용할 수 있음 |
허용되지 않음 |
| CWG 2144 | C++11 | { } 가 동일한 위치에서 함수 본문이나 초기화자 모두 될 수 있음 |
선언자 식별자의
타입에 따라 구분됨 |
| CWG 2145 | C++98 | 함수 정의의 declarator 는 괄호로 묶을 수 없음 | 허용됨 |
| CWG 2259 | C++11 |
괄호로 묶인 타입 이름에 대한 모호성 해결 규칙이
람다 표현식을 포함하지 않음 |
포함됨 |
| CWG 2430 | C++98 |
클래스 정의 내 멤버 함수 정의에서,
해당 클래스의 타입이 CWG 이슈 1824 의 해결로 인해 반환 타입이나 매개변수 타입이 될 수 없음 |
함수 본문에서만
검사 수행 |
| CWG 2760 | C++98 |
생성자의 함수 본문이 생성자의 일반 함수 본문에
명시되지 않은 초기화들을 포함하지 않음 |
이러한 초기화들도
포함함 |
| CWG 2831 | C++20 |
requires-clause
를 가진 함수 정의가
비템플릿 함수를 정의할 수 있음 |
금지됨 |
| CWG 2846 | C++23 | 명시적 객체 멤버 함수가 클래스 외부 정의를 가질 수 없음 | 허용됨 |
| CWG 2915 | C++23 | 이름 없는 명시적 객체 매개변수가 void 타입을 가질 수 있음 | 금지됨 |
참고 항목
|
C documentation
for
Declaring functions
|