I'm FanJae.

[C++] Function II 본문

C++/Basic

[C++] Function II

FanJae 2024. 8. 8. 00:37

※ 본 포스트는 코드누리 C++ Basic 강의 내용을 보고 정리한 포스트입니다.

 

1. 함수 템플릿(Function template)

 

1-1. 함수 오버로딩의 단점

함수 오버로딩(Function overloading) - 관련 내용 링크

- 인자의 형태(타입, 개수)가 다르면 동일 이름의 함수를 여러개 만들 수 있다.

Ex) square(int), square(dobule)

 

※ square 함수를 만들 때, 인자 타입과 반환 타입만 다르고 구현이 동일(유사)한 함수를 여러개 만들어야 한다.

 

1-2. 함수 템플릿(Function template)의 정의

- C++ 언어의 해결책은 구현이 동일(유사)한 함수가 여러 개 필요하면 함수를 만들지 않는다.

- 대신, 함수를 생성하는 틀(템플릿)을 만들자는 것이다.

// template<typename T> 이것도 많이 사용된다.
template<class T>
T square(T a)
{
    return a * a;
}

int main()
{
    square<int>(3);
    square<double>(3.4);
}

 

- 위와 같이 사용하면, 타입에 상관없이 동일한 형태의 함수를 생성해 준다.

- 이 코드는 실질적으로 아래와 같이 변환된다.

 

1-2-1. Template instantiation(템플릿 인스턴스화)

int square(int a)
{
    return a*a;
}
double square(double a)
{
    return a*a;
}

- Template(틀) 을 사용해서 컴파일러가 실제 함수를 생성하는 과정이다.

- main()에서 square에 대해서 int, double 형을 사용했기 때문에 위와 같이 2개의 실제 함수를 생성한 것이다.

 

- 단순히 template을 생성하는 것만으로는 코드가 생성되지 않는다.

- 어셈블리 레벨에서 확인할때, main() 밖에 없다.

 

※ 즉, 함수 템플릿을 만들고 사용하지 않으면, 인스턴스화(Instantiation)되지 않는다.

 

- main()에서 square<int>(3);과 같이 호출할 때, 함수가 생성됨을 확인할 수 있다.

 

1-2-2. 함수 템플릿을 사용하는 방법

① 템플릿 인자(타입)을 명시적으로 전달 

square<int>(3);

- 사용자가 전달한 타입으로 함수를 생성한다.

 

② 템플릿 인자(타입)을 생략

square(3)

- 컴파일러가 함수 인자를 보고 타입을 추론한다.

 

③ 코드 폭발(Code Bloat)

- 템플릿이 너무 많은 타입에 대해 인스턴스화(Instantiation) 되어서 코드 메모리가 증가하는 현상

char c = 3;
short s = 3;
int n = 3;


/* 주석 친 것과 같이 호출하면, 총 3개 타입에 대한 인스턴스화가 일어난다.
square(c);
square(s);
square(n);
*/

- 위 예제는 총 3개 타입에 대한 인스턴스화가 일어난다. 이에 따라서 코드 메모리가 증가할 수 있다.

- 이런 부분의 경우는 아래와 같이 수정할 수 있다.

 

char c = 3;
short s = 3;
int n = 3;

square<int>(c);
square<int>(s);
square<int>(n);

 

- 이와 같이 명시하여 1개의 타입에 대한 인스턴스화를 할 수 있다.

 

④ 함수 템플릿을 만드는 방법

//template <typename T>
template<class T>
T square(T a)
{
  return a * a;
}

auto square(auto a) // C++ 20
{
  return a * a;
}

- C++20 부터는 auto를 이용한 방법도 사용이 가능하다.

 

※ 함수와 함수 템플릿은 별개의 개념이다. square는 함수 템플릿이다. 따라서, square의 주소는 구할 수 없다.

하지만, square <타입>은 구할 수 있다.

printf("%p\n",&square); // X
printf("%p\n",square<int>); // O

2. 인라인 함수(inline Function)

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

inline int Add2(int a, int b)
{
    return a + b;
}

int main()
{
    int ret1 = Add1(1, 2);
    int ret2 = Add2(1, 2);
}

 

- 일반 함수는 호출시 함수 인자를 약속된 장소에 넣는다. 이후, 함수로 이동한다.

- 이에 반면 인라인 함수는 함수 호출 코드를 함수의 기계어 코드로 바로 치환해 달라는 의미이다.

즉, 함수 본문이 삽입된다.

 

2-1. 어셈블리어 코드를 통한 분석

Compile Explorer에서 /Ob1 명령어를 입력하면, 인라인 치환이 적용된다.

- Add2는 함수를 생성하지 않았고, 목적 코드가 줄어든 것을 확인할 수 있다.

 

※ 인라인 함수는 빠르다는 장점이 있지만, 크기가 큰 함수를 여러번 치환하면 목적코드(실행파일)이 커질 수 있다. 단, 크기가 작은 함수는 인라인 치환시 목적코드를 줄이기도 한다.

※ 위 처럼, 함수 포인터와 같이 Add2의 주소를 받아오면, Add2를 생성하는 것을 확인할 수 있다.

단, 이것이 인라인 함수 치환 여부에 영향을 주지는 않는다.


3. Linkage

// mylib.h
int Add1(int a, int b);

inline int Add2(int a, int b);

template <class T>
T Add3(T a, T b);

// mylib.cpp
int Add1(int a, int b)
{
    return a+b;
}

inline int Add2(int a, int b)
{
    return a+b;
}

template <class T>
T Add3(T a, T b)
{
    return a+b;
}

// using_mylib.cpp
#include "mylib.h"
int main()
{
    Add1(1,2);
    Add2(1,2);
    Add3(1,2);
}

- 위 예제에서 인라인 함수나 템플릿 함수 호출시 link Error가 발생한다.

 

3-1. 이러한 문제가 발생하는 이유

- 컴파일러는 모든 소스파일을 동시에 컴파일하지 않는다.

- 파일별로 분리해서 컴파일한다.

// using_mylib.cpp
#include "mylib.h"
int main()
{
    Add1(1,2); // call "Add1 주소"
    Add2(1,2);
    Add3(1,2);
}

- call "Add1 주소" -> 컴파일 할 때는 Add1의 주소를 알 수 없다. 하지만 이는 링커가 이를 채워줄 것이다.

- 하지만, Add2 를 컴파일 할때, 호출 위치에 인라인 함수의 본문을 삽입해야 한다.

- 이러한 문제는 Add3를 컴파일 할때 템플릿 인스턴스화를 진행하면서 유사한 문제가 발생한다.

 

즉, 함수 구현 자체를 헤더 파일에 넣어야 한다.

 

3-2. Internal linkage & External linkage

Internal linkage : 심볼(함수, 변수이름)이 선언된 같은 컴파일 단위(파일) 에서만 사용 가능한 경우

Ex. 인라인 함수, 템플릿, static 키워드 등

External linkage : 프로젝트 내 모든 컴파일 단위에서 사용 가능한 경우

Ex. 일반 함수, 전역 변수 등

 

※ 개인적으로는 Linkage에 대한 개념을 익히는데 매우 고전했다. 컴파일 단위라는 것에 대한 개념 이해가 부족했고, Linkage를 학습하면서 다시 제대로 이 개념을 익히고 갔다.

'C++ > Basic' 카테고리의 다른 글

[C++] Function III  (0) 2024.08.08
[C++] Function III  (0) 2024.08.08
[C++] Function I  (0) 2024.08.07
[C++] C++ 에서 새롭게 추가된 Type & Variable  (0) 2024.08.06
[C++] 표준 입출력 정리  (0) 2024.08.06
Comments