컴파일러가 일반 함수보다 람다를 더 잘 최적화 할 수있는 이유는 무엇입니까? 그런 겁니까? 인라인에

그의 책에서 The C++ Standard Library (Second Edition)Nicolai Josuttis는 람다는 컴파일러가 일반 함수보다 더 잘 최적화 할 수 있다고 말합니다.

또한 C ++ 컴파일러는 일반적인 기능보다 람다를 더 잘 최적화합니다. (213 페이지)

왜 그런 겁니까?

인라인에 관해서는 더 이상 차이가 없어야한다고 생각했습니다. 내가 생각할 수있는 유일한 이유는 컴파일러가 람다와 더 나은 로컬 컨텍스트를 가질 수 있고 더 많은 가정을하고 더 많은 최적화를 수행 할 수 있기 때문입니다.



답변

그 이유는 람다는 함수 객체 이므로 함수 템플릿으로 전달하면 해당 객체에 대한 새로운 함수가 인스턴스화되기 때문입니다. 따라서 컴파일러는 람다 호출을 간단하게 인라인 할 수 있습니다.

반면 함수의 경우 오래된주의 사항이 적용됩니다. 함수 포인터 가 함수 템플릿으로 전달되고 컴파일러는 전통적으로 함수 포인터를 통해 호출을 인라인하는 데 많은 문제가 있습니다. 그들은 이론적으로 인라인 될 수 있지만, 주변 기능뿐만 아니라 인라인 될 경우에만 가능합니다.

예를 들어 다음 함수 템플릿을 고려하십시오.

template <typename Iter, typename F>
void map(Iter begin, Iter end, F f) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}

다음과 같이 람다로 호출하십시오.

int a[] = { 1, 2, 3, 4 };
map(begin(a), end(a), [](int n) { return n * 2; });

이 인스턴스화 결과 (컴파일러가 작성) :

template <>
void map<int*, _some_lambda_type>(int* begin, int* end, _some_lambda_type f) {
    for (; begin != end; ++begin)
        *begin = f.operator()(*begin);
}

… 컴파일러는 _some_lambda_type::operator ()이를 쉽게 알아서 호출 할 수 있습니다. (그 함수 호출 map임의 의 새로운 인스턴스 생성하는 다른 람다 map각, λ는 구별 타입을 갖기 때문에).

그러나 함수 포인터로 호출하면 인스턴스화는 다음과 같습니다.

template <>
void map<int*, int (*)(int)>(int* begin, int* end, int (*f)(int)) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}

… 여기에서 f호출 할 때마다 다른 주소를 가리 키 map므로 컴파일러가 f주변 호출을 인라인 하지 않으면 컴파일러가 호출을 인라인 할 수 없으므로 map컴파일러가 f특정 함수로 해석 할 수 있습니다 .


답변

“함수”를 알고리즘에 전달할 때 실제로 함수에 대한 포인터를 전달하므로 함수에 대한 포인터를 통해 간접 호출을 수행해야하기 때문입니다. 람다를 사용할 때 객체에서 해당 유형에 대해 특별히 인스턴스화 된 템플릿 인스턴스로 전달되고 람다 함수에 대한 호출은 함수 포인터를 통한 호출이 아니므로 직접 인라인 될 수 있습니다.