함수(C++)

함수는 일부 작업을 수행하는 코드 블록입니다. 함수는 호출자가 함수에 인수를 전달할 수 있도록 하는 입력 매개 변수를 필요에 따라 정의할 수 있습니다. 함수는 필요에 따라 출력으로 값을 반환할 수 있습니다. 함수는 이상적으로 함수의 기능을 명확하게 설명하는 이름을 사용하여 재사용 가능한 단일 블록에서 일반 작업을 캡슐화하는 데 유용합니다. 다음 함수는 호출자에서 두 개의 정수를 수락하고 해당 합계를 반환합니다. ab는 형식int매개 변수입니다.

int sum(int a, int b)
{
    return a + b;
}

함수는 프로그램의 여러 위치에서 호출하거나 호출할 수 있습니다. 함수에 전달되는 값은 인수이며, 해당 형식은 함수 정의의 매개 변수 형식과 호환되어야 합니다.

int main()
{
    int i = sum(10, 32);
    int j = sum(i, 66);
    cout << "The value of j is" << j << endl; // 108
}

함수 길이에 실제적인 제한은 없지만 좋은 디자인은 잘 정의된 단일 작업을 수행하는 함수를 목표로 합니다. 복잡한 알고리즘은 가능하면 이해하기 쉬운 더 간단한 함수로 세분화해야 합니다.

클래스 범위에서 정의되는 함수는 멤버 함수라고 합니다. 다른 언어와 달리 C++에서는 네임스페이스 범위(암시적 전역 네임스페이스 포함)에서 함수를 정의할 수도 있습니다. 이러한 함수를 자유 함수 또는 멤버가 아닌 함수라고 합니다. 표준 라이브러리에서 광범위하게 사용됩니다.

함수가 오버로드될 수 있습니다. 즉, 함수의 다른 버전이 형식 매개 변수의 수 및/또는 형식이 다른 경우 동일한 이름을 공유할 수 있습니다. 자세한 내용은 함수 오버로드를 참조하세요.

함수 선언의 요소

최소 함수 선언 은 컴파일러에 추가 지침을 제공하는 선택적 키워드와 함께 반환 형식, 함수 이름 및 매개 변수 목록(비어 있을 수 있음)으로 구성됩니다. 다음 예제는 함수 선언입니다.

int sum(int a, int b);

함수 정의는 중괄호 사이의 모든 코드인 선언과 본문으로 구성됩니다.

int sum(int a, int b)
{
    return a + b;
}

함수 선언과 뒤에 오는 세미콜론은 프로그램의 여러 위치에 나타날 수 있으며, 각 변환 단위에서 해당 함수를 호출하기 전에 나타나야 합니다. 함수 정의는 ODR(단일 정의 규칙)에 따라 프로그램에 한 번만 나타나야 합니다.

함수 선언의 필수 요소는 다음과 같습니다.

  1. 함수가 반환하는 값의 형식을 지정하거나 void 값이 반환되지 않는 경우를 지정하는 반환 형식입니다. C++11 auto 에서는 반환 문에서 형식을 유추하도록 컴파일러에 지시하는 유효한 반환 형식입니다. C++14에서도 decltype(auto) 허용됩니다. 자세한 내용은 아래의 반환 형식에서 형식 추론을 참조하세요.

  2. 함수 이름 - 문자 또는 밑줄로 시작하고 공백을 포함할 수 없습니다. 일반적으로 표준 라이브러리 함수 이름의 선행 밑줄은 private 멤버 함수 또는 코드에 사용할 수 없는 비멤버 함수를 나타냅니다.

  3. 매개 변수 목록 - 중괄호로 구분되거나 쉼표로 구분된 0개 이상의 매개 변수 집합으로, 함수 본문 내에서 값에 액세스할 수 있는 형식 및 로컬 이름(선택 사항)을 지정합니다.

함수 선언의 선택적 요소는 다음과 같습니다.

  1. constexpr - 함수의 반환 값이 컴파일 시간에 계산할 수 있는 상수 값임을 나타냅니다.

    constexpr float exp(float x, int n)
    {
        return n == 0 ? 1 :
            n % 2 == 0 ? exp(x * x, n / 2) :
            exp(x * x, (n - 1) / 2) * x;
    };
    
  2. 링크 사양 extern 또는 static.

    //Declare printf with C linkage.
    extern "C" int printf( const char *fmt, ... );
    
    

    자세한 내용은 번역 단위 및 연결을 참조하세요.

  3. inline - 함수에 대한 모든 호출을 함수 코드 자체로 바꾸도록 컴파일러에 지시합니다. 인라인 처리는 성능이 중요한 코드 섹션에서 함수가 신속하게 실행되고 반복적으로 호출되는 시나리오의 성능 향상에 도움이 됩니다.

    inline double Account::GetBalance()
    {
        return balance;
    }
    

    자세한 내용은 인라인 함수를 참조하세요.

  4. noexcept 함수가 예외를 throw할 수 있는지 여부를 지정하는 식입니다. 다음 예제에서는 식이 .로 계산되는 경우 함수가 is_pod 예외를 truethrow하지 않습니다.

    #include <type_traits>
    
    template <typename T>
    T copy_object(T& obj) noexcept(std::is_pod<T>) {...}
    

    자세한 내용은 noexcept을 참조하세요.

  5. (멤버 함수만 해당) 함수인지 여부를 지정하는 cv 한정자입니다 constvolatile.

  6. (멤버 함수만 해당) virtual또는 overridefinal. virtual은 파생 클래스에서 함수를 재정의할 수 있도록 지정합니다. override는 파생 클래스의 함수가 가상 함수를 재정의하고 있음을 의미합니다. final은 이후 파생 클래스에서 함수를 재정의할 수 없음을 의미합니다. 자세한 내용은 Virtual Functions를 참조하세요.

  7. (멤버 함수만 해당) static 멤버 함수에 적용됨은 함수가 클래스의 개체 인스턴스와 연결되지 않음을 의미합니다.

  8. (비정적 멤버 함수만 해당) 암시적 개체 매개 변수(*this)가 rvalue 참조와 lvalue 참조일 때 선택할 함수의 오버로드를 컴파일러에 지정하는 ref-qualifier입니다. 자세한 내용은 함수 오버로드를 참조하세요.

다음 그림에서는 함수 정의의 일부분을 보여 줍니다. 음영 처리된 영역은 함수 본문입니다.

Diagram of the parts of a function definition.
함수 정의 부분

함수 정의

함수 정의는 변수 선언, 문 및 식을 포함하는 중괄호로 묶인 선언과 함수 본문으로 구성됩니다. 다음 예제에서는 전체 함수 정의를 보여줍니다.

    int foo(int i, std::string s)
    {
       int value {i};
       MyClass mc;
       if(strcmp(s, "default") != 0)
       {
            value = mc.do_something(i);
       }
       return value;
    }

본문 내에 선언된 변수는 지역 변수 또는 지역이라고 합니다. 이러한 변수는 함수가 종료될 때 범위를 벗어나므로 함수는 지역에 대한 참조를 반환할 수 없습니다.

    MyClass& boom(int i, std::string s)
    {
       int value {i};
       MyClass mc;
       mc.Initialize(i,s);
       return mc;
    }

const 및 constexpr 함수

멤버 함수 const 를 선언하여 함수가 클래스의 데이터 멤버 값을 변경할 수 없도록 지정할 수 있습니다. 멤버 함수를 선언 const하면 컴파일러가 const-correctness를 적용하는 데 도움이 됩니다. 누군가가 실수로 선언된 const함수를 사용하여 개체를 수정하려고 하면 컴파일러 오류가 발생합니다. 자세한 내용은 const를 참조하세요.

생성되는 값이 컴파일 시간에 결정될 수 있는 경우와 같이 constexpr 함수를 선언합니다. constexpr 함수는 일반적으로 일반 함수보다 빠르게 실행됩니다. 자세한 내용은 constexpr을 참조하세요.

함수 템플릿

함수 템플릿은 클래스 템플릿과 유사하며 템플릿 인수에 따라 구체적인 함수를 생성합니다. 대부분의 경우 템플릿은 형식 인수를 유추할 수 있으므로 명시적으로 지정할 필요가 없습니다.

template<typename Lhs, typename Rhs>
auto Add2(const Lhs& lhs, const Rhs& rhs)
{
    return lhs + rhs;
}

auto a = Add2(3.13, 2.895); // a is a double
auto b = Add2(string{ "Hello" }, string{ " World" }); // b is a std::string

자세한 내용은 함수 템플릿을 참조하세요.

함수 매개 변수 및 인수

함수에는 0개 이상 형식의 쉼표로 구분된 매개 변수 목록이 있으며, 각각에는 함수 본문 내에서 액세스할 수 있는 이름이 있습니다. 함수 템플릿은 추가 형식이나 값 매개 변수를 지정할 수 있습니다. 호출자는 형식이 매개 변수 목록과 호환되는 구체적인 값인 인수를 전달합니다.

기본적으로 인수는 값으로 함수에 전달됩니다. 즉, 함수는 전달할 개체의 복사본을 받는다는 의미입니다. 큰 개체의 경우 복사본을 만들려면 비용이 많이 들 수 있으며 항상 만들 필요는 없습니다. 인수가 참조(특히 lvalue 참조)로 전달되도록 하려면 매개 변수에 참조 수량자를 추가합니다.

void DoSomething(std::string& input){...}

함수에서 참조로 전달된 인수를 수정하면 로컬 복사본이 아니라 원래 개체가 수정됩니다. 함수가 이러한 인수를 수정하지 못하도록 하려면 매개 변수를 const&로 한정합니다.

void DoSomething(const std::string& input){...}

C++ 11: rvalue-reference 또는 lvalue-reference로 전달되는 인수를 명시적으로 처리하려면 매개 변수에서 double-ampersand를 사용하여 범용 참조를 나타냅니다.

void DoSomething(const std::string&& input){...}

매개 변수 선언 목록에서 단일 키워드 void 로 선언된 함수는 키워드 void 가 인수 선언 목록의 첫 번째이자 유일한 멤버인 한 인수를 사용하지 않습니다. 목록의 다른 위치에 있는 형식 void 의 인수는 오류를 생성합니다. 예를 들면 다음과 같습니다.

// OK same as GetTickCount()
long GetTickCount( void );

여기서 설명한 대로 인수를 지정 void 하는 것은 불법이지만 형식에서 void 파생된 형식(예: 포인터 void 및 배열)은 인수 선언 목록의 void아무 곳에나 나타날 수 있습니다.

기본 인수

함수 서명에서 마지막 매개 변수에 기본 인수를 할당할 수 있습니다. 즉, 일부 다른 값을 지정하려는 경우가 아니면 함수를 호출할 때 호출자가 인수를 제외할 수 있습니다.

int DoSomething(int num,
    string str,
    Allocator& alloc = defaultAllocator)
{ ... }

// OK both parameters are at end
int DoSomethingElse(int num,
    string str = string{ "Working" },
    Allocator& alloc = defaultAllocator)
{ ... }

// C2548: 'DoMore': missing default parameter for parameter 2
int DoMore(int num = 5, // Not a trailing parameter!
    string str,
    Allocator& = defaultAllocator)
{...}

자세한 내용은 기본 인수를 참조하세요.

함수 반환 형식

함수는 다른 함수 또는 기본 제공 배열을 반환하지 않을 수 있습니다. 그러나 이러한 형식 또는 함수 개체를 생성하는 람다에 대한 포인터를 반환할 수 있습니다. 이러한 경우를 제외하고 함수는 범위에 있는 모든 형식의 값을 반환하거나 값을 반환하지 않을 수 있습니다. 이 경우 반환 형식이 됩니다 void.

후행 반환 형식

"일반" 반환 형식은 함수 서명의 왼쪽에 있습니다. 후행 반환 형식은 서명의 오른쪽에 있으며 연산자가 앞에 옵니다->. 후행 반환 형식은 반환 값의 형식이 템플릿 매개 변수에 따라 달라지는 경우 함수 템플릿에서 특히 유용합니다.

template<typename Lhs, typename Rhs>
auto Add(const Lhs& lhs, const Rhs& rhs) -> decltype(lhs + rhs)
{
    return lhs + rhs;
}

후행 반환 형식과 함께 사용되는 경우 auto decltype 식이 생성하는 모든 개체 틀 역할을 하며 형식 추론을 수행하지 않습니다.

함수 지역 변수

함수 본문 내에서 선언된 변수를 지역 변수 또는 단순히 로컬이라고 부릅니다. 비정적 지역은 함수 본문 내에만 표시되며, 스택에서 선언될 경우 함수가 종료되면 범위를 벗어납니다. 지역 변수를 생성하고 값으로 반환하는 경우 컴파일러는 일반적으로 명명된 반환 값 최적화 를 수행하여 불필요한 복사 작업을 방지할 수 있습니다. 지역 변수를 참조로 반환하는 경우 해당 참조를 사용하려는 호출자의 모든 시도가 지역이 제거된 후 수행되므로 컴파일러에서 경고가 발생합니다.

C++에서는 지역 변수를 정적으로 선언할 수 있습니다. 이 변수는 함수 본문 내에만 표시되지만 함수의 모든 인스턴스에 대해 변수의 단일 복사본이 존재합니다. 로컬 정적 개체는 atexit로 지정된 종료 중에 소멸됩니다. 프로그램의 제어 흐름이 정적 개체의 선언을 건너뛰었기 때문에 정적 개체가 생성되지 않은 경우에는 해당 개체를 소멸하려고 하지 않습니다.

반환 형식의 형식 추론(C++14)

C++14에서는 후행 반환 형식을 제공하지 않고도 함수 본문에서 반환 형식을 유추하도록 컴파일러에 지시하는 데 사용할 auto 수 있습니다. auto 항상 값별 반환을 추론합니다. auto&&를 사용하여 참조를 추론하도록 컴파일러에 지시합니다.

이 예제 auto 에서는 lhs 및 rhs 합계의 비 const 값 복사본으로 추론됩니다.

template<typename Lhs, typename Rhs>
auto Add2(const Lhs& lhs, const Rhs& rhs)
{
    return lhs + rhs; //returns a non-const object by value
}

추론하는 auto 형식의 const-ness는 유지되지 않습니다. 반환 값이 인수의 const-ness 또는 ref-ness를 유지해야 하는 전달 함수의 경우 형식 유추 규칙을 사용하고 decltype 모든 형식 정보를 유지하는 키워드를 사용할 decltype(auto) 수 있습니다. decltype(auto)은 왼쪽에 있는 일반 반환 값이나 후행 반환 값으로 사용할 수 있습니다.

다음 예제( N3493의 코드 기반)는 템플릿이 인스턴스화될 때까지 알려지지 않은 반환 형식에서 함수 인수를 완벽하게 전달하도록 설정하는 데 사용되는 방법을 보여 decltype(auto) 줍니다.

template<typename F, typename Tuple = tuple<T...>, int... I>
decltype(auto) apply_(F&& f, Tuple&& args, index_sequence<I...>)
{
    return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...);
}

template<typename F, typename Tuple = tuple<T...>,
    typename Indices = make_index_sequence<tuple_size<Tuple>::value >>
   decltype( auto)
    apply(F&& f, Tuple&& args)
{
    return apply_(std::forward<F>(f), std::forward<Tuple>(args), Indices());
}

함수에서 여러 값 반환

함수에서 두 개 이상의 값을 반환하는 다양한 방법이 있습니다.

  1. 명명된 클래스 또는 구조체 개체의 값을 캡슐화합니다. 호출자에게 클래스 또는 구조체 정의를 표시해야 합니다.

    #include <string>
    #include <iostream>
    
    using namespace std;
    
    struct S
    {
        string name;
        int num;
    };
    
    S g()
    {
        string t{ "hello" };
        int u{ 42 };
        return { t, u };
    }
    
    int main()
    {
        S s = g();
        cout << s.name << " " << s.num << endl;
        return 0;
    }
    
  2. std::tuple 또는 std::p air 개체를 반환합니다.

    #include <tuple>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    tuple<int, string, double> f()
    {
        int i{ 108 };
        string s{ "Some text" };
        double d{ .01 };
        return { i,s,d };
    }
    
    int main()
    {
        auto t = f();
        cout << get<0>(t) << " " << get<1>(t) << " " << get<2>(t) << endl;
    
        // --or--
    
        int myval;
        string myname;
        double mydecimal;
        tie(myval, myname, mydecimal) = f();
        cout << myval << " " << myname << " " << mydecimal << endl;
    
        return 0;
    }
    
  3. Visual Studio 2017 버전 15.3 이상(모드 이상에서 /std:c++17 사용 가능): 구조적 바인딩을 사용합니다. 구조화된 바인딩의 장점은 반환 값을 저장하는 변수가 선언되는 동시에 초기화되므로 경우에 따라 훨씬 더 효율적일 수 있습니다. 문 auto[x, y, z] = f(); 에서 대괄호는 전체 함수 블록의 범위에 있는 이름을 도입하고 초기화합니다.

    #include <tuple>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    tuple<int, string, double> f()
    {
        int i{ 108 };
        string s{ "Some text" };
        double d{ .01 };
        return { i,s,d };
    }
    struct S
    {
        string name;
        int num;
    };
    
    S g()
    {
        string t{ "hello" };
        int u{ 42 };
        return { t, u };
    }
    
    int main()
    {
        auto[x, y, z] = f(); // init from tuple
        cout << x << " " << y << " " << z << endl;
    
        auto[a, b] = g(); // init from POD struct
        cout << a << " " << b << endl;
        return 0;
    }
    
  4. 반환 값 자체를 사용하는 것 외에도 함수가 호출자가 제공하는 개체의 값을 수정하거나 초기화할 수 있도록 참조를 통해 전달할 매개 변수 수를 정의하여 값을 "반환"할 수 있습니다. 자세한 내용은 참조 형식 함수 인수를 참조하세요.

함수 포인터

C++은 C 언어와 동일한 방식으로 함수 포인터를 지원합니다. 그러나 일반적으로 함수 개체를 사용하면 형식이 보다 더 안전합니다.

함수 포인터 형식을 반환하는 함수를 선언하는 경우 함수 포인터 형식에 대한 별칭을 선언하는 데 사용하는 것이 좋습니다 typedef . 예를 들면 다음과 같습니다.

typedef int (*fp)(int);
fp myFunction(char* s); // function returning function pointer

다른 방법으로, 식별자(위 예제에서 fp)를 함수 이름 및 인수 목록으로 대체하여 함수 선언의 올바른 구문을 함수 포인터의 선언자 구문에서 추론할 수 있습니다. 예제는 다음과 같습니다.

int (*myFunction(char* s))(int);

위의 선언은 위의 선언과 typedef 동일합니다.

참고 항목

함수 오버로드
변수 인수 목록이 있는 함수
명시적으로 기본값 및 삭제된 함수
함수에 대한 인수 종속 이름(Koenig) 조회
기본 인수
인라인 함수