함수
함수 (Functions)
함수는 문장들을 묶어서 이름과 매개변수 목록에 연결해 둔 C++의 엔터티예요. 반복해서 쓰고 싶은 작업을 한 번 정의해 두면, 원하는 곳에서 이름만 불러서 실행할 수 있죠.
출처: cppreference
본문
함수의 생김새
가장 기본적인 모양부터 볼게요. 함수는 이름, 매개변수 목록, 반환 타입, 그리고 몸통(body)으로 이루어져 있어요.
// function name: "isodd"
// parameter list has one parameter, with name "n" and type int
// the return type is bool
bool isodd(int n)
{ // the body of the function begins
return n % 2;
} // the body of the function ends
함수를 호출하면, 예를 들어 함수 호출 표현식(function-call expression)에서 호출하면, 매개변수가 인자(argument)로 초기화되고(호출 위치에서 주거나 기본값을 쓰죠) 함수 몸통의 문장이 실행돼요. 매개변수 목록이 ...로 끝나면 인자를 추가로 더 넣을 수 있는데, 이런 함수를 가변 인자 함수(variadic function)라고 불러요.
int main()
{
for (int arg : {-3, -2, -1, 0, 1, 2, 3})
std::cout << isodd(arg) << ' '; // isodd called 7 times, each
// time n is copy-initialized from arg
}
함수 호출 표현식에서 한정되지 않은(unqualified) 함수 이름을 찾을 때는 "인자 종속 조회(argument-dependent lookup, ADL)"라는 별도의 규칙이 적용돼요.
함수는 return으로 끝날 수도 있고, 예외(exception)를 던져서 끝날 수도 있어요.
함수는 코루틴(coroutine)이 될 수도 있는데, 이 경우 실행을 잠시 중단했다가 나중에 다시 재개할 수 있어요. (C++20부터)
함수 선언은 어떤 스코프에서든 나타날 수 있지만, 함수 정의는 네임스페이스 스코프에서만, 멤버 함수와 friend 함수의 경우엔 클래스 스코프에서만 나타날 수 있어요. 클래스 본문에 friend 지정자 없이 선언된 함수는 클래스 멤버 함수인데, 이런 함수들은 추가적인 속성이 많아서 멤버 함수 문서에서 자세히 다룹니다.
함수는 객체가 아닙니다
함수는 객체가 아니어서, 함수의 배열은 없고 함수를 값으로 전달하거나 함수에서 함수를 반환할 수도 없어요. 함수에 대한 포인터와 참조는(메인 함수와 대부분의 표준 라이브러리 함수 제외, C++20부터) 허용되며, 함수 자체를 쓸 수 없는 자리에 대신 사용할 수 있어요. 그래서 이런 함수들을 "주소를 가질 수 있다(addressable)"고 말합니다.
함수의 타입과 오버로딩
각 함수는 타입을 가지는데, 함수의 반환 타입, 모든 매개변수의 타입(배열→포인터, 함수→포인터 변환 후, 매개변수 목록 참고), noexcept 여부(C++17부터), 그리고 비정적 멤버 함수라면 cv-한정과 ref-한정(C++11부터)으로 구성돼요. 함수 타입에는 언어 연결(language linkage)도 있어요. cv-한정된 함수 타입은 없습니다(int f() const; 같은 cv-한정 함수의 타입이나 std::string const f(); 같은 cv-한정 타입을 반환하는 함수의 타입과는 혼동하지 마세요). 함수 타입의 별칭(alias)에 cv-한정자를 붙이면 무시돼요.
같은 스코프에 있는 여러 함수가 같은 이름을 가질 수 있어요. 단, 매개변수 목록이 다르고(비정적 멤버 함수라면 cv/ref 한정(C++11부터)도) 달라야 하죠. 이걸 함수 오버로딩(function overloading)이라고 해요. 반환 타입과 noexcept 지정(C++17부터)만 다른 함수 선언은 오버로드할 수 없어요. 오버로드된 함수의 주소를 구하는 방식도 따로 정해져 있습니다.
C++은 람다 표현식(lambda-expression)으로 익명 함수를 구현해요. (C++11부터)
함수 객체 (Function objects)
함수 호출 표현식은 함수 lvalue뿐 아니라 함수 포인터와, 함수 호출 연산자를 오버로드하거나 함수 포인터로 변환 가능한 클래스 타입의 값(람다 표현식 포함, C++11부터)도 지원해요. 이런 타입들을 통틀어 FunctionObject라고 부르며, C++ 표준 라이브러리 곳곳에서 두루 쓰입니다. 예를 들어 BinaryPredicate나 Compare의 용도를 떠올려 보시면 돼요.
표준 라이브러리는 미리 정의된 여러 함수 객체 템플릿과 새로운 것을 조합하는 방법도 제공해요. std::less, std::mem_fn, std::bind, std::function(C++11부터), std::not_fn(C++17부터), std::bind_front(C++20부터), std::bind_back, std::move_only_function(C++23부터), std::copyable_function, std::function_ref(C++26부터) 같은 것들이죠.
더 알아보기 (Learn more)
- 멤버 함수 (member functions) — 클래스 멤버 함수의 추가 속성
- 함수 선언 (function declaration) — 함수를 선언하고 정의하는 규칙
- 함수 오버로딩 — 같은 이름의 함수를 구분하는 규칙
- 람다 표현식 — 익명 함수 만들기