[c++] 재귀 함수가 인라인 일 수 있습니까?

inline int factorial(int n)
{
    if(!n) return 1;
    else return n*factorial(n-1);
}

내가 이것을 읽었을 때 컴파일러가 올바르게 처리하지 않으면 위의 코드가 “무한 컴파일”로 이어질 것이라는 것을 알았습니다.

컴파일러는 함수를 인라인할지 여부를 어떻게 결정합니까?



답변

첫째, inline함수 의 스펙은 힌트 일뿐입니다. 컴파일러는 inline한정자의 유무를 완전히 무시할 수 있으며 종종 무시합니다 . 즉, 상기와 더불어, 컴파일러 는 무한 루프 언 롤링 수만큼 재귀 함수를 인라인. 단순히 기능을 “롤”할 수있는 수준에 제한을 두어야합니다.

최적화 컴파일러는 다음 코드를 사용할 수 있습니다.

inline int factorial(int n)
{
    if (n <= 1)
    {
        return 1;
    }
    else
    {
        return n * factorial(n - 1);
    }
}

int f(int x)
{
    return factorial(x);
}

이 코드로 :

int factorial(int n)
{
    if (n <= 1)
    {
        return 1;
    }
    else
    {
        return n * factorial(n - 1);
    }
}

int f(int x)
{
    if (x <= 1)
    {
        return 1;
    }
    else
    {
        int x2 = x - 1;
        if (x2 <= 1)
        {
            return x * 1;
        }
        else
        {
            int x3 = x2 - 1;
            if (x3 <= 1)
            {
                return x * x2 * 1;
            }
            else
            {
                return x * x2 * x3 * factorial(x3 - 1);
            }
        }
    }
}

이 경우 기본적으로 함수를 3 번 ​​인라인했습니다. 일부 컴파일러 이 최적화를 수행합니다. MSVC ++는 재귀 함수 (최대 20 개)에서 수행 될 인라인 레벨을 조정하는 설정을 가지고 있습니다.


답변

실제로 컴파일러가 지능적으로 작동하지 않으면 inlined 함수의 사본을 재귀 적으로 삽입 하여 무한히 큰 코드를 작성 하려고 할 수 있습니다 . 그러나 대부분의 최신 컴파일러는이를 인식합니다. 그들은 다음 중 하나를 수행 할 수 있습니다.

  1. 전혀 함수를 인라인하지 않습니다
  2. 특정 깊이까지 인라인하고 종료되지 않은 경우 표준 함수 호출 규칙을 사용하여 함수의 개별 인스턴스를 호출하십시오. 이렇게하면 많은 일반적인 경우를 고성능 방식으로 처리 할 수 ​​있으며 통화 깊이가 큰 드문 경우에는 대체 할 수 있습니다. 이것은 또한 함수 코드의 인라인 버전과 별도 버전을 유지한다는 것을 의미합니다.

사례 2의 경우 많은 컴파일러 #pragma에서이 작업을 수행 할 최대 깊이를 지정하도록 설정할 수 있습니다. gcc 에서는 명령 행에서이 정보를 전달할 수도 있습니다 --max-inline-insns-recursive(자세한 내용은 여기 참조 ).


답변

AFAIK GCC는 가능한 경우 재귀 함수에서 테일 콜 제거를 수행합니다. 그러나 함수는 꼬리 재귀가 아닙니다.


답변

컴파일러는 호출 그래프를 만듭니다. 사이클 자체가 호출되는 것을 감지하면 함수는 더 이상 특정 깊이 (n = 1, 10, 100, 컴파일러가 조정 된대로) 후에 인라인되지 않습니다.


답변

일부 재귀 함수는 루프로 변환되어 효과적으로 무한히 인라인됩니다. 나는 gcc가 이것을 할 수 있다고 생각하지만 다른 컴파일러에 대해서는 모른다.


답변

이것이 일반적으로 작동하지 않는 이유는 이미 제공된 답변을 참조하십시오.

“각주”로서 템플릿 메타 프로그래밍을 사용하여 찾고있는 효과를 얻을 수 있습니다 (적어도 예제로 사용중인 계승에 대해서는) . Wikipedia에서 붙여 넣기 :

template <int N>
struct Factorial
{
    enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0>
{
    enum { value = 1 };
};


답변

컴파일러는 이러한 종류의 것들을 감지하고 방지하기 위해 호출 그래프를 만듭니다. 따라서 함수가 자체적으로 호출되고 인라인이 아니라는 것을 알 수 있습니다.

그러나 주로 인라인 키워드와 컴파일러 스위치에 의해 제어됩니다 (예를 들어 키워드 없이도 작은 함수를 자동 인라인으로 만들 수 있습니다). 코드에서 생성 한 호출