Namespaces
Variants

Function declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

함수 선언은 함수 이름과 그 타입을 도입합니다. 함수 정의는 함수 이름/타입을 함수 본문과 연결합니다.

목차

함수 선언

함수 선언은 어떤 범위에서든 나타날 수 있습니다. 클래스 범위에서의 함수 선언은 ( 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 참조)

1) 일반 함수 선언자 구문.
2) 후행 반환 타입 선언. 이 경우 decl-specifier-seq 은 반드시 키워드 auto 를 포함해야 합니다.
noptr-declarator - 유효한 declarator 이지만, * , & , 또는 && 로 시작하는 경우 괄호로 둘러싸여야 함
parameter-list - 함수 매개변수의 쉼표로 구분된 목록 (비어 있을 수 있음, 자세한 내용은 아래 참조)
attr - (C++11부터) attributes 목록. 이러한 속성은 함수 자체가 아닌 함수의 타입에 적용됩니다. 함수에 대한 속성은 선언자 내 식별자 뒤에 나타나며, 선언 시작 부분에 나타나는 속성(있는 경우)과 결합됩니다.
cv - const/volatile 한정자, 비정적 멤버 함수 선언에서만 허용됨
ref - (C++11부터) ref-qualification, 비정적 멤버 함수 선언에서만 허용됨
except -

dynamic exception specification

(C++11까지)

dynamic exception specification 또는 noexcept specification

(C++11부터)
(C++17까지)

noexcept specification

(C++17부터)
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 ) 인 경우, 반환 타입은 반환문에서 사용된 피연산자가 decltype 으로 감싸진 것과 동일하게 얻어집니다:

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  (선택 사항) this decl-specifier-seq declarator

(2) (C++23부터)
attr  (선택 사항) decl-specifier-seq declarator = initializer (3)
attr  (선택 사항) decl-specifier-seq abstract-declarator  (선택 사항) (4)

attr  (선택 사항) this decl-specifier-seq abstract-declarator  (선택 사항)

(5) (C++23부터)
attr  (선택 사항) decl-specifier-seq abstract-declarator  (선택 사항) = initializer (6)
void (7)
1) 명명된(형식) 매개변수를 선언합니다. decl-specifier-seq declarator 의 의미에 대해서는 declarations 를 참조하십시오.
int f ( int a, int * p, int ( * ( * x ) ( double ) ) [ 3 ] ) ;
2) 명명된 explicit object parameter 를 선언합니다.
3) 기본값 을 갖는 명명된(형식) 매개변수를 선언합니다.
int f ( int a = 7 , int * p = nullptr, int ( * ( * x ) ( double ) ) [ 3 ] = nullptr ) ;
4) 이름 없는 매개변수를 선언합니다.
int f ( int , int * , int ( * ( * ) ( double ) ) [ 3 ] ) ;
5) 이름 없는 explicit object parameter 를 선언합니다.
6) 기본값 을 가진 이름 없는 매개변수를 선언합니다.
int f ( int = 7 , int * = nullptr, int ( * ( * ) ( double ) ) [ 3 ] = nullptr ) ;
7) 함수가 매개변수를 받지 않음을 나타내며, 빈 매개변수 목록과 정확한 동의어입니다: int f ( void ) ; int f ( ) ; 는 동일한 함수를 선언합니다.
void 는 빈 매개변수 목록과 동일한 유일한 구문이며, 다른 방식의 void 매개변수 사용은 잘못된 형식입니다:
잘못된 사용법 예시
여러 매개변수가 존재하는 경우 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를 가진 멤버 함수에는 다음과 같은 제한이 있습니다:

  • 함수는 static 일 수 없습니다.
  • 함수는 virtual 일 수 없습니다.
  • 함수의 선언자에 cv ref 가 포함될 수 없습니다.
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 는 다음과 같이 결정됩니다:

  1. 각 매개변수의 타입 (함수 parameter packs 포함) (since C++11) 은 자체 parameter declaration 에서 결정됩니다.
  2. 각 매개변수의 타입을 결정한 후, " T 의 배열" 타입이나 함수 타입 T 인 매개변수는 " T 에 대한 포인터"로 조정됩니다.
  3. 매개변수 타입 목록을 생성한 후, 함수 타입을 형성할 때 매개변수 타입을 수정하는 모든 최상위 cv-qualifiers 가 삭제됩니다.
  4. 변환된 매개변수 타입들의 결과 목록과 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 "로 지정합니다:

  • 예외 명세가 non-throwing 인 경우, 선언된 함수의 타입은
    "derived-declarator-type-list noexcept function of
    parameter-type-list cv  (optional) ref   (optional) returning 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 T "로 주어지면
( T 는 이 경우 auto 여야 함):

(C++11부터)
  • 예외 명세가 non-throwing 인 경우, 선언된 함수의 타입은
    "derived-declarator-type-list noexcept function of
    parameter-type-list cv  (선택사항) ref   (선택사항) returning trailing "입니다.
(C++17부터)
  • 그렇지 않으면 (C++17 이전) 그렇지 않으면 (C++17부터) 선언된 함수의 타입은
    "derived-declarator-type-list function of
    parameter-type-list cv  (선택사항) ref   (선택사항) returning trailing "입니다.

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 int FIC(int) const;
FIC f;     // 오류: 멤버 함수를 선언하지 않음
struct S
{
    FIC f; // 정상
};
FIC S::*pm = &S::f; // 정상

함수 시그니처

모든 함수에는 시그니처가 있습니다.

함수의 시그니처는 함수 이름과 parameter-type-list 로 구성됩니다. 또한 다음 예외를 제외하고는 포함하는 namespace 도 시그니처에 포함됩니다:

  • 함수가 member function 인 경우, 해당 함수의 시그니처는 포함된 네임스페이스 대신 함수가 멤버로 속한 클래스를 포함합니다. 또한 시그니처는 다음과 같은 구성 요소가 존재할 경우 포함합니다:
  • cv
  • ref
(C++11부터)
  • 후행 requires
  • 함수가 후행 requires 절을 가진 비템플릿 friend 함수인 경우, 해당 시그니처는 바깥 네임스페이스 대신 바깥 클래스를 포함합니다. 시그니처는 또한 후행 requires 절도 포함합니다.
(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 이후)
1) 제약 조건이 없는 함수 정의.
2) 제약 조건이 있는 함수 정의.
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부터)
1) 일반 함수 본문.
3) 명시적으로 기본 설정된 함수 정의.
4) 명시적으로 삭제된 함수 정의.
5) 오류 메시지가 포함된 명시적으로 삭제된 함수 정의.
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부터) 여야 하며, 기본 인자 를 가져서는 안 됩니다.

명시적으로 기본 설정된 특수 멤버 함수 F1 은 암시적으로 선언되었을 해당 특수 멤버 함수 F2 와 다음과 같이 다를 수 있습니다:

  • F1 F2 는 서로 다른 ref 및/또는 except 를 가질 수 있습니다.
  • 만약 F2 const C & 타입의 비객체 매개변수를 갖는 경우, F1 의 해당 비객체 매개변수는 C& 타입일 수 있습니다.
  • 만약 F2 가 "참조형 C " 타입의 암시적 객체 매개변수를 가지고 있다면, F1 은 (서로 다른 타입일 수 있는) "참조형 C " 타입의 명시적 객체 매개변수 를 가지는 명시적 객체 멤버 함수일 수 있으며, 이 경우 F1 의 타입은 F2 의 타입과 F1 의 타입이 추가 매개변수를 가지고 있다는 점에서 다를 수 있습니다.
(C++23부터)

F1 의 타입이 F2 의 타입과 선행 규칙에서 허용된 방식 이외의 방식으로 다른 경우:

  • 만약 F1 이 대입 연산자이고, F1 의 반환 타입이 F2 의 반환 타입과 다르거나 F1 의 비객체 매개변수 타입이 참조가 아닌 경우, 프로그램의 형식이 올바르지 않습니다.
  • 그렇지 않고 F1 이 첫 번째 선언에서 명시적으로 기본값으로 지정된 경우, 삭제된 것으로 정의됩니다.
  • 그 외의 경우, 프로그램의 형식이 올바르지 않습니다.

첫 번째 선언에서 명시적으로 기본값으로 설정된 함수는 암시적으로 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) 함수로 정의될 수 있습니다. 삭제된 함수는 삭제된 함수로만 오버라이드할 수 있으며, 삭제되지 않은 함수는 삭제되지 않은 함수로만 오버라이드할 수 있습니다.

만약 string-literal 이 존재한다면, 구현체는 삭제의 근거를 보여주거나 대안을 제안하는 결과 진단 메시지의 일부로 해당 텍스트를 포함하도록 권장된다.

(since C++26)

함수가 오버로드된 경우, 오버로드 해결 이 먼저 수행되며, 삭제된 함수가 선택된 경우에만 프로그램이 잘못된 형식입니다:

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 { 또는 = (C++26부터) 로 시작하는 경우의 모호함이 있을 때, 이 모호함은 declarator identifier 의 타입을 확인하여 해결됩니다 noptr-declarator :

  • 타입이 함수 타입인 경우, 모호한 토큰 시퀀스는 함수 본문으로 처리됩니다.
  • 그렇지 않은 경우, 모호한 토큰 시퀀스는 초기화자로 처리됩니다.
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__); // 오류: 매개변수 목록은 선언자의 일부입니다
#include <iostream>
void Foo() { std::cout << __func__ << ' '; }
struct Bar
{
    Bar() { std::cout << __func__ << ' '; }
    ~Bar() { std::cout << __func__ << ' '; }
    struct Pub { Pub() { std::cout << __func__ << ' '; } };
};
int main()
{
    Foo();
    Bar bar;
    Bar::Pub pub;
}

가능한 출력:

Foo Bar Pub ~Bar
(C++11 이후)

함수 계약 지정자

함수 선언과 람다 표현식 은 일련의 함수 계약 지정자  를 포함할 수 있으며, 각 지정자는 다음 구문을 가집니다:

pre attr  (선택적) ( predicate ) (1)
post attr  (선택적) ( predicate ) (2)
post attr  (선택적) ( identifier result-attr  (선택적) : predicate ) (3)
1) 사전 조건 어서션(precondition assertion) 을 도입합니다.
2,3) 사후 조건 단언문(postcondition assertion)을 도입합니다.
2) 단언문이 결과에 바인딩되지 않습니다.
3) 단언문이 결과에 바인딩됩니다.
attr - 도입된 계약 어설션에 속하는 속성 목록
predicate - 모든 표현식 (괄호로 묶지 않은 쉼표 표현식 제외)
identifier - 결과를 참조하는 식별자
result-attr - 결과 바인딩에 속하는 속성 목록


사전 조건 단언과 사후 조건 단언을 통칭하여 함수 계약 단언  이라고 합니다.

함수 계약 단언은 계약 단언 이 함수와 연관된 것입니다. 함수 계약 단언의 술어는 그 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 재선언 D contract-specs 를 전혀 가지지 않거나, D 에서 도달 가능한 첫 번째 선언 F 와 동일한 contract-specs 를 가져야 합니다. D F 가 서로 다른 번역 단위에 있는 경우, D 가 명명된 모듈에 연결된 경우에만 진단이 필요합니다.

선언 F1 이 하나의 번역 단위에서 func 의 첫 번째 선언이고, 선언 F2 가 다른 번역 단위에서 func 의 첫 번째 선언인 경우, F1 F2 는 동일한 contract-specs 를 지정해야 하며, 진단은 요구되지 않습니다.

두 개의 contract-specs 는 동일한 순서로 동일한 함수 계약 지정자로 구성된 경우 동일합니다.

함수 계약 지정자 C1 이 함수 선언 D1 에 있을 때, 다음의 모든 조건이 충족되면 함수 계약 지정자 C2 가 함수 선언 D2 에 있는 것과 동일합니다:

  • C1 C2 predicate 는 다음 재명명을 제외하고 선언 D1 D2 에 각각 위치한 함수 정의에 놓였을 때 one-definition rule 을 충족할 것입니다( D1 D2 가 서로 다른 번역 단위에 있는 경우, 각 predicate 내에 정의된 해당 개체들은 단일 정의를 가진 단일 개체인 것처럼 동작함):
    • 선언된 함수의 매개변수 재명명
    • 선언된 함수를 포함하는 템플릿의 템플릿 매개변수 재명명
    • 결과 바인딩(있는 경우)의 재명명
  • C1 C2 모두 identifier 를 가지거나 둘 다 가지지 않습니다.

이 조건이 오직 두 개의 람다 표현식 비교로 인해 충족되지 않고, 이 람다 표현식들이 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) 이유가 명시된 삭제된 함수

키워드

default , delete , pre , post

예제

#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