서적 정리/Effective C++

30.인라인 함수는 미주알고주알 따져서 이해해 두자

민돌이2 2021. 12. 19. 18:50

사실 나는 인라인 함수를 잘 안쓴다. 구현하기 바빠서 그런 것도 있지만 필요성을 못느낀다. 비슷한 의미로 람다 함수도 손이 안간다. 장점보단 단점이 크게 보이기 때문이다. 코드가 함수로 점프하는게 아닌 그냥 가져와서 빠르다고는 하지만, 코드크기도 부풀려지고 굳이란 생각도 있다. 무엇보다 솔직히 언제 써야 최고의 성능을 발휘하는지도 모른다.

 

인라인 함수는 함수처럼 보이고 함수처럼 동작하고 매크로보다 안전하고(2장 참고) 함수 호출 시 발생하는 오버헤드 걱정도 없는 장점이 있다. 또한 컴파일러 최적화는 함수 호출이 없는 어찌보면 절차적 언어 같은 코드에 적용되도록 설계되었기 때문에, 함수 본문에 대해 문맥별(context-specific) 최적화를 걸기 용이해 진다.

실제 대부분의 컴파일러는 아웃라인(outline) 함수 호출(*주 1)에 대해 이런 최적화를 적용하지 않는다.

 

인라인은 컴파일러에 대해 요청을 하는 것이지, 명령이 아니다. 책이 말을 돌려서 쓴거지 inline을 붙이지 않아도 암시적으로 컴파일러가 인라인화 하여 사용하기도 하고, 명시적으로 선언할 수 도 있다.

 

암시적 인라인은 말 그대로 개발자가 inline 키워드를 붙이지 않아도 컴파일러가 인라인화 한다는 것이다.

클래스 정의 안에 함수를 바로 정의해 넣으면 컴파일러는 인라인 함수로 사용할 수 있다.

class Person
{
public:
	int Age() const { return theAge; } //암시적 inline 요청

private:
	int theAge;
};

 

명시적 인라인은 inline 키워드를 붙이면 된다.

대표적인 예로 표준 라이브러리의 max 템플릿이 인라인으로 구현되어 있다.

template<typename T>
inline const T& std::max(const T& a, const T& b)
{ return a < b ? b : a; }

 

템플릿은 대개 헤더 파일 안에 정의하기 때문에 템플릿은 반드시 인라인 함수여야 한다고 생각할 수 있는데 아니다.

인라인 함수는 몇몇 컴파일러는 제외하면 대부분의 컴파일러에서는 헤더 파일에 들어가야 하는게 맞다. 인라인 함수 호출을 그 함수의 본문으로 바꿔치기하려면, 일단 컴파일러가 인라인 함수가 어떤 형태인지 알아야 하니까 당연하다.

템플릿 역시 대체적으로 헤더 파일에 들어 있어야 한다. 템플릿이 사용되는 부분에서 해당 템플릿을 인스턴스로 만들려면 어떻게 생겼는지를 컴파일러가 알고 있어야 하기 때문이다. 템플릿도 몇몇 컴파일러를 제외하면 헤더에 있어야한다.

하지만, 템플릿 인스턴스화와 인라인은 완전히 별개로 관련도 없다. 템플릿으로 부터 만들어지는 모든 함수가 인라인 함수였으면 할 때 인라인을 붙이는 것이지 템플릿이 인라인일 필요가 없다면 인라인을 붙이지 않아도 된다.

 

명시적으로 inline 키워드를 붙였다고 해도 컴파일러가 봤을 때 루프가 들어 있다거나 재귀 함수인 경우 등으로 복잡한 함수는 인라인으로 만들지 않는다.

또한 virtual 역시 인라인화 하지 않는다. virtual의 의미가 어떤 함수를 호출할지 결정하는 작업을 실행 중에 한다는 뜻인데 inline의 의미는 함수 호출 위치에 호출된 함수를 끼워 넣는 작업을 프로그램 실행 전에 한다라는 뜻이니 애초에 성립되지 않는다.

 

인라인 조건을 갖추었는데도, 컴파일러가 인라인 함수의 본문에 대해 코드를 만드는 경우가 있다(인라인화와 별개로 구현부 코드를 별개로 만든다는 의미).

예를 들어 인라인 함수의 주소를 취하는 코드가 있다면, 컴파일러는 이 코드를 위해 아웃라인 함수 본문을 만들 수 밖에 없다. 컴파일 중에는 주소가 존재할리가 없으니 함수의 주소를 포인터가 어떻게 가리키겠는가?

inline void f() { ... } //컴파일러가 반드시 인라인해 준다고 가정

void (*pf)() = f; //pf는 f를 가리키는 함수 포인터

f(); //함수 호출. 인라인될 것이다.

pf(); //이 호출은 인라인되지 않을 것이다.

 

인라인은 디버거 입장에서 곤란해 하는 비호감 대상이라고 한다. 몇몇 빌드 환경은 인라인 함수의 디버깅을 지원하긴 하지만, 대다수의 환경에서는 지원하지 않는다.

반드시 인라인해야 하는 함수(46장 참고) 혹은 정말 단순한 함수(Get 함수 같은 것)에 한해서만 인라인 함수로 선언하는 것으로 시작하자.

 

이것만은 잊지 말자

함수 인라인은 작고, 자주 호출되는 함수에 대해서만 하자. 디버깅 및 라이브러리의 바이너리 업그레이드가 용이해지고, 코드 부풀림 현상이 최소화되며, 프로그램의 속력이 빨라질 여지가 생긴다.

함수 템플릿이 대개 헤더 파일에 들어간다는 일반적인 부분만 생각해서 이들을 inline으로 선언하면 안된다.

 

(*주 1) 아웃라인 함수 호출

일반적인 함수 호출을 뜻한다. 함수 본문을 그대로 끼워 넣는 인라인 함수와 반대라고 이해하면 된다. 그냥 인라인 함수의 뜻을 부각 시키기 위해 사용한다고 봐도 된다.

728x90