인라인 함수(C++)

inline 이 키워드(keyword) 컴파일러가 해당 함수에 대한 각 호출 대신 함수 정의 내의 코드를 대체하도록 제안합니다.

이론적으로 인라인 함수를 사용하면 함수 호출과 관련된 오버헤드를 제거하기 때문에 프로그램을 더 빠르게 만들 수 있습니다. 함수를 호출하려면 스택에서 반환 주소를 푸시하고, 인수를 스택으로 푸시하고, 함수 본문으로 이동한 다음, 함수가 완료되면 반환 명령을 실행해야 합니다. 이 프로세스는 함수를 인라인 처리하여 제거됩니다. 또한 컴파일러는 인라인으로 확장된 함수와 그렇지 않은 함수를 최적화할 수 있는 다른 기회도 있습니다. 인라인 함수의 단점은 프로그램의 전체 크기가 증가할 수 있다는 것입니다.

인라인 코드 대체는 컴파일러의 재량에 따라 수행됩니다. 예를 들어 해당 주소가 사용되거나 컴파일러가 너무 크다고 판단하는 경우 컴파일러는 함수를 인라인으로 처리하지 않습니다.

클래스 선언의 본문에 정의된 함수는 암시적으로 인라인 함수입니다.

예시

다음 클래스 선언에서 생성자는 클래스 선언 Account 의 본문에 정의되어 있으므로 인라인 함수입니다. 멤버 함수는 GetBalanceDepositWithdraw 해당 정의에 지정 inline 됩니다. inline 키워드(keyword) 클래스 선언의 함수 선언에서 선택 사항입니다.

// account.h
class Account
{
public:
    Account(double initial_balance)
    {
        balance = initial_balance;
    }

    double GetBalance() const;
    double Deposit(double amount);
    double Withdraw(double amount);

private:
    double balance;
};

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

inline double Account::Deposit(double amount)
{
    balance += amount;
    return balance;
}

inline double Account::Withdraw(double amount)
{
    balance -= amount;
    return balance;
}

참고 항목

클래스 선언에서 함수는 키워드(keyword) 없이 inline 선언되었습니다. inline 클래스 선언에서 키워드(keyword) 지정할 수 있습니다. 결과는 동일합니다.

주어진 인라인 멤버 함수는 모든 컴파일 단위에서 동일한 방식으로 선언되어야 합니다. 인라인 함수의 정의는 정확히 하나만 있어야 합니다.

해당 함수에 대한 정의에 지정자가 포함되어 있지 않으면 클래스 멤버 함수는 기본적으로 외부 링크로 inline 설정됩니다. 앞의 예제에서는 지정자를 사용하여 이러한 함수를 명시적으로 선언할 필요가 없음을 inline 보여 줍니다. 함수 정의에서 사용하면 inline 인라인 함수로 처리될 것을 컴파일러에 제안합니다. 그러나 해당 함수를 호출한 후와 같이 inline 함수를 다시 지정할 수는 없습니다.

inline, __inline__forceinline

inline__inline 지정자는 함수가 호출되는 각 위치에 함수 본문의 복사본을 삽입하도록 컴파일러에 제안합니다.

인라인 확장 또는 인라인이라고 하는 삽입은 컴파일러의 비용 혜택 분석에서 가치가 있는 것으로 표시되는 경우에만 발생합니다. 인라인 확장은 더 큰 코드 크기의 잠재적 비용으로 함수 호출 오버헤드를 최소화합니다.

__forceinline 키워드(keyword) 비용 혜택 분석을 재정의하고 프로그래머의 판단에 의존합니다. 를 사용할 때는 주의해야 합니다 __forceinline. 무차별적으로 사용하면 __forceinline 성능이 저하되거나 경우에 따라 성능이 저하되는 코드가 커질 수 있습니다(예: 더 큰 실행 파일의 페이징 증가로 인해).

컴파일러는 인라인 확장 옵션과 키워드를 제안으로 처리합니다. 함수가 인라인될 것이라는 보장은 없습니다. 키워드(keyword) 경우에도 __forceinline 컴파일러가 특정 함수를 인라인으로 설정하도록 강제할 수 없습니다. 컴파일 /clr할 때는 함수에 적용되는 보안 특성이 있는 경우 컴파일러가 함수를 인라인으로 표시하지 않습니다.

이전 버전과의 호환성을 위해 _inline_forceinline은 각각 __inline__forceinline의 동의어입니다. 단 컴파일러 옵션 /Za(언어 확장 사용 안 함)가 지정된 경우는 예외입니다.

inline 키워드(keyword) 인라인 확장이 선호된다는 것을 컴파일러에 알릴 수 있습니다. 그러나 컴파일러는 이를 무시할 수 있습니다. 이 동작이 발생할 수 있는 두 가지 경우는 다음과 같습니다.

  • 재귀 함수.
  • 변환 단위의 다른 위치에서 포인터를 통해 참조되는 함수.

이러한 이유는 컴파일러에 의해 결정된 대로 다른 이유와 마찬가지로 인라인 처리에 방해가 될 수 있습니다. 함수가 inline 인라인 처리되도록 지정자에 의존하지 마세요.

컴파일러는 헤더 파일에 정의된 인라인 함수를 확장하는 대신 둘 이상의 변환 단위에서 호출 가능한 함수로 만들 수 있습니다. 컴파일러는 링커에 대해 생성된 함수를 표시하여 ODR(One-definition-rule) 위반을 방지합니다.

일반 함수와 마찬가지로 인라인 함수에서는 인수 계산 순서가 정의되지 않습니다. 실제로 일반 함수 호출 프로토콜을 사용하여 전달될 때 인수 평가 순서와 다를 수 있습니다.

/Ob 컴파일러 최적화 옵션을 사용하여 인라인 함수 확장이 실제로 발생하는지 여부에 영향을 줍니다.
/LTCG 는 소스 코드에서 요청되었는지 여부에 관계없이 모듈 간 인라인을 수행합니다.

예 1

// inline_keyword1.cpp
// compile with: /c
inline int max(int a, int b)
{
    return a < b ? b : a;
}

클래스의 멤버 함수는 키워드(keyword) 사용 inline 하거나 클래스 정의 내에 함수 정의를 배치하여 인라인으로 선언할 수 있습니다.

예제 2

// inline_keyword2.cpp
// compile with: /EHsc /c
#include <iostream>

class MyClass
{
public:
    void print() { std::cout << i; }   // Implicitly inline

private:
    int i;
};

Microsoft 전용

__inline 키워드(keyword) 동일합니다inline.

다음과 같은 __forceinline경우 컴파일러는 함수를 인라인할 수 없습니다.

  • 함수 또는 해당 호출자는 (디버그 빌드에 대한 기본 옵션)을 사용하여 /Ob0 컴파일됩니다.
  • 함수 및 호출자가 다양한 형식의 예외 처리(한 경우 C++ 예외 처리, 다른 경우 구조적 예외 처리)를 사용합니다.
  • 함수에 가변 인수 목록이 있습니다.
  • 함수는 인라인 어셈블리를 사용하며 , /O1또는 /O2./Ox
  • 함수는 재귀적이며 설정되지 #pragma inline_recursion(on) 않았습니다. pragma를 사용하면 재귀 함수가 기본 깊이 16번 호출로 인라인 처리됩니다. 인라인 깊이를 줄이려면 pragma를 사용합니다 inline_depth .
  • 가상 함수이며 실제로 호출됩니다. 가상 함수에 대한 직접 호출은 인라인 처리할 수 있습니다.
  • 프로그램에서 함수의 주소를 사용하고 함수에 대한 포인터를 통해 호출합니다. 주소가 사용된 함수에 대한 직접 호출은 인라인 처리할 수 있습니다.
  • 함수도 한정자를 사용하여 naked__declspec 표시됩니다.

컴파일러가 선언된 __forceinline함수를 인라인할 수 없는 경우 다음을 제외하고 수준 1 경고를 생성합니다.

  • 함수는 /Od 또는 /Ob0을 사용하여 컴파일됩니다. 이러한 경우에는 인라인 처리가 필요하지 않습니다.
  • 이 함수는 포함된 라이브러리 또는 다른 번역 단위에서 외부적으로 정의되거나 가상 호출 대상 또는 간접 호출 대상입니다. 컴파일러는 현재 변환 단위에서 찾을 수 없는 인라인이 아닌 코드를 식별할 수 없습니다.

재귀 함수는 pragma에서 지정한 inline_depth 깊이까지 최대 16개의 호출로 인라인 코드로 바꿀 수 있습니다. 해당 깊이 이후 재귀 함수 호출은 함수의 인스턴스 호출로 처리됩니다. 인라인 추론에서 재귀 함수를 검사하는 깊이는 16을 초과할 수 없습니다. pragma는 inline_recursion 현재 확장 중인 함수의 인라인 확장을 제어합니다. 관련 정보는 인라인 함수 확장(/Ob) 컴파일러 옵션을 참조하세요.

Microsoft 전용 종료

지정자 사용에 대한 자세한 내용은 다음을 inline 참조하세요.

인라인 함수 사용 시기

인라인 함수는 데이터 멤버에 대한 액세스를 제공하는 함수와 같은 작은 함수에 가장 적합합니다. 짧은 함수는 함수 호출의 오버헤드에 민감합니다. 더 긴 함수는 호출 및 반환 시퀀스에서 비례적으로 더 적은 시간을 보내고 인라인 처리의 이점을 줄입니다.

클래스는 Point 다음과 같이 정의할 수 있습니다.

// when_to_use_inline_functions.cpp
// compile with: /c
class Point
{
public:
    // Define "accessor" functions
    // as reference types.
    unsigned& x();
    unsigned& y();

private:
    unsigned _x;
    unsigned _y;
};

inline unsigned& Point::x()
{
    return _x;
}

inline unsigned& Point::y()
{
    return _y;
}

좌표 조작이 이러한 클래스의 클라이언트에서 비교적 일반적인 작업이라고 가정하고, 일반적으로 오버헤드를 절약할 수 있는 inline 두 개의 접근자 함수(xy 이전 예제)를 지정합니다.

  • 함수 호출(개체의 주소를 스택에 전달 및 배치하는 매개 변수 포함)
  • 호출자의 스택 프레임의 보존
  • 새 스택 프레임 설정
  • 반환 값 전달
  • 이전 스택 프레임 복원
  • 반환 값

인라인 함수와 매크로 비교

매크로에는 함수와 몇 가지 공통점이 있습니다 inline . 그러나 중요한 차이점이 있습니다. 다음 예제를 참조하세요.

#include <iostream>

#define mult1(a, b) a * b
#define mult2(a, b) (a) * (b)
#define mult3(a, b) ((a) * (b))

inline int multiply(int a, int b)
{
    return a * b;
}

int main()
{
    std::cout << (48 / mult1(2 + 2, 3 + 3)) << std::endl; // outputs 33
    std::cout << (48 / mult2(2 + 2, 3 + 3)) << std::endl; // outputs 72
    std::cout << (48 / mult3(2 + 2, 3 + 3)) << std::endl; // outputs 2
    std::cout << (48 / multiply(2 + 2, 3 + 3)) << std::endl; // outputs 2

    std::cout << mult3(2, 2.2) << std::endl; // no warning
    std::cout << multiply(2, 2.2); // Warning C4244	'argument': conversion from 'double' to 'int', possible loss of data
}
33
72
2
2
4.4
4

다음은 매크로와 인라인 함수 간의 몇 가지 차이점입니다.

  • 매크로는 항상 인라인으로 확장됩니다. 그러나 인라인 함수는 컴파일러가 최적의 작업을 수행하는 것으로 판단할 때만 인라인 처리됩니다.
  • 매크로로 인해 예기치 않은 동작이 발생할 수 있으며 이로 인해 버그가 미묘해질 수 있습니다. 예를 들어 식 mult1(2 + 2, 3 + 3) 은 11로 2 + 2 * 3 + 3 계산되지만 예상 결과는 24로 확장됩니다. 겉보기에 유효한 수정은 함수 매크로의 두 인수 주위에 괄호를 #define mult2(a, b) (a) * (b)추가하여 현재 문제를 해결하지만 더 큰 식의 일부일 때 여전히 놀라운 동작을 일으킬 수 있습니다. 앞의 예제에서 설명한 대로 매크로 #define mult3(a, b) ((a) * (b))를 정의하여 문제를 해결할 수 있습니다.
  • 인라인 함수는 컴파일러에서 의미 체계를 처리하는 반면 전처리기는 동일한 이점 없이 매크로를 확장합니다. 매크로는 형식이 안전하지 않지만 함수는 안전합니다.
  • 인라인 함수에 인수로 전달된 식은 한 번 계산됩니다. 매크로에 인수로 전달된 식은 경우에 따라 여러 번 계산할 수 있습니다. 예를 들어 다음을 고려합니다.
#include <iostream>

#define sqr(a) ((a) * (a))

int increment(int& number)
{
    return number++;
}

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

int main()
{
    int c = 5;
    std::cout << sqr(increment(c)) << std::endl; // outputs 30
    std::cout << c << std::endl; // outputs 7

    c = 5;
    std::cout << square(increment(c)) << std::endl; // outputs 25
    std::cout << c; // outputs 6
}
30
7
25
6

이 예제에서는 식 sqr(increment(c)) 이 확장되면서 함수 increment 가 두 번 호출됩니다((increment(c)) * (increment(c))). 이로 인해 두 번째 호출 increment 이 6을 반환하므로 식이 30으로 계산됩니다. 부작용이 포함된 식은 매크로에서 사용될 때 결과에 영향을 줄 수 있으며, 완전히 확장된 매크로를 검사하여 동작이 의도된 경우 검사 수 있습니다. 대신 인라인 함수 square 를 사용한 경우 함수 increment 는 한 번만 호출되고 25의 올바른 결과를 얻습니다.

참고 항목

noinline
auto_inline