inline 지정자

inline 지정자 (inline specifier)

함수의 decl-specifier-seq 에 inline 지정자를 쓰면 그 함수를 인라인 함수(inline function) 로 선언하는 거예요.

  • 클래스/구조체/union 정의 안에서 온전히 정의된 함수 — 멤버 함수든 비멤버 friend 함수든 — 은 명명된 모듈에 붙지 않는 한(attached to a named module, C++20부터) 암시적으로 인라인 함수예요.
  • 첫 선언에서 constexpr 또는 consteval(C++20부터)로 선언된 함수는 암시적으로 인라인 함수예요.
  • 삭제된(deleted) 함수는 암시적으로 인라인 함수예요. 그 (삭제된) 정의가 둘 이상의 번역 단위에 나타날 수 있기 때문이에요. (C++11부터)

정적 저장 기간을 가진 변수(정적 클래스 멤버 또는 네임스페이스 범위 변수)의 decl-specifier-seq 에 inline 지정자를 쓰면 그 변수를 인라인 변수(inline variable) 로 선언해요.

  • 첫 선언에서 constexpr로 선언된 정적 데이터 멤버는 암시적으로 인라인 변수예요. (C++17부터)

출처: cppreference

본문

설명 (Explanation)

인라인 함수 또는 인라인 변수(C++17부터)는 다음 성질을 가져요.

  • 인라인 함수나 변수(C++17부터)의 정의는 접근되는 번역 단위에서 도달 가능해야 해요 (반드시 접근 지점보다 앞일 필요는 없어요).
  • 외부 링키지(external linkage)를 가진 인라인 함수나 변수(예: static으로 선언되지 않은 것)는 다음 추가 성질을 가져요.
    • 프로그램 안에 인라인 함수나 변수의 정의가 여러 개 있을 수 있어요. 단 각 정의가 서로 다른 번역 단위에 나타나야 하고, (비정적 인라인 함수와 변수에 대해) 모든 정의가 같아야 해요. 예를 들어 인라인 함수나 인라인 변수는 여러 소스 파일에 포함되는 헤더 파일에 정의될 수 있어요.
    • 모든 번역 단위에서 inline으로 선언되어야 해요.
    • 모든 번역 단위에서 같은 주소를 가져요.
  • 인라인 함수 안에서:
    • 모든 함수 정의의 함수 지역 정적 객체는 모든 번역 단위에 걸쳐 공유돼요 (모두 한 번역 단위에 정의된 같은 객체를 가리켜요).
    • 모든 함수 정의에 정의된 타입들도 모든 번역 단위에서 같아요.
  • 네임스페이스 범위의 인라인 const 변수는 기본적으로 외부 링키지를 가져요 (비인라인 비휘발성 const-한정 변수와 달라요). (C++17부터)

inline 키워드의 원래 의도는 최적화 프로그램에게 함수 호출보다 인라인 치환(inline substitution)을 선호하라고 알려주는 신호였어요. 즉 함수 호출 CPU 명령으로 제어 흐름을 함수 본문으로 넘기는 대신, 함수 본문의 복사본을 호출 없이 실행하라는 뜻이었죠. 이렇게 하면 함수 호출이 만드는 오버헤드(인자 전달과 결과 회수)를 피할 수 있지만, 함수 코드를 여러 번 반복해야 하므로 실행 파일이 더 커질 수 있어요.

인라인 치환은 표준 의미론에서 관찰할 수 없기 때문에, 컴파일러는 inline으로 표시되지 않은 함수에도 인라인 치환을 자유로이 쓸 수 있고, inline으로 표시된 함수에도 함수 호출을 만들어 낼 수 있어요. 이런 최적화 선택은 앞서 나열한 다중 정의와 static 공유에 관한 규칙을 바꾸지 않아요.

C++98부터 함수에 대한 inline 키워드의 의미가 "인라인이 선호된다"에서 "여러 정의가 허용된다"로 바뀌었기 때문에, 그 의미는 변수에게로 확장됐어요. (C++17부터)

참고 (Notes)

  • 외부 링키지를 가진 인라인 함수나 변수(C++17부터)가 서로 다른 번역 단위에서 다르게 정의되면 프로그램은 규칙 위반이며 진단이 요구되지 않아요.
  • inline 지정자는 블록 범위(다른 함수 안)의 함수나 변수 선언에는 쓸 수 없어요.
  • inline 지정자는 이미 그 번역 단위에서 비인라인으로 정의된 함수나 변수를 다시 선언할 수 없어요.
  • 암시적으로 생성되는 멤버 함수와 첫 선언에서 defaulted 로 선언된 멤버 함수는 클래스 정의 안에 정의된 다른 함수와 마찬가지로 인라인이에요.
  • 인라인 함수가 서로 다른 번역 단위에서 선언되면, 각 번역 단위 끝에서 누적된 기본 인자 집합이 같아야 해요.
  • C에서는 인라인 함수가 모든 번역 단위에서 inline으로 선언될 필요가 없고(많아야 하나는 비인라인 또는 extern inline일 수 있어요), 함수 정의가 같을 필요도 없고(어느 것이 호출되는지에 따라 프로그램 동작이 달라지면 지정되지 않음), 함수 지역 static 도 같은 함수의 다른 정의들 사이에서 구별돼요.
  • 인라인 정적 멤버에 대한 추가 규칙은 정적 데이터 멤버를 봐요.
  • 인라인 변수는 C++ 코드를 header-only 라이브러리로 패키징하는 데 가장 큰 걸림돌을 제거해요. (C++17부터)

기능 테스트 매크로로는 __cpp_inline_variables가 값 201606L(C++17)로 인라인 변수를 나타내요.

키워드 (Keywords)

inline

예제 (Example)

헤더 "example.h":

#ifndef EXAMPLE_H
#define EXAMPLE_H

#include <atomic>

// function included in multiple source files must be inline
inline int sum(int a, int b)
{
    return a + b;
}

// variable with external linkage included in multiple source files must be inline
inline std::atomic<int> counter(0);

#endif

소스 파일 #1:

#include "example.h"

int a()
{
    ++counter;
    return sum(1, 2);
}

소스 파일 #2:

#include "example.h"

int b()
{
    ++counter;
    return sum(3, 4);
}

결함 보고 (Defect reports)

동작 변경 결함 보고로 주요하게 조정된 내용은 이래요. friend 함수 선언이 인라인 함수가 아님에도 inline 지정자를 쓰는 것이 금지됨(CWG 281), 같은 번역 단위에서 비인라인 정의가 앞선 함수의 inline 선언이 규칙 위반이 됨(CWG 317), 인라인 함수 안에 정의된 타입이 모든 번역 단위에서 같게 됨(CWG 765), 인라인 함수의 문자열 리터럴 공유 요구가 제거됨(CWG 1823), 첫 선언에서 constexpr이 아닌 정적 데이터 멤버는 암시적 인라인이 아니게 됨(CWG 2531) 등이에요.

더 알아보기 (Learn more)