Fundamental of CS/: : C++

[Functional Programming] Lambda This

Jay.P Morgan 2024. 7. 31. 20:11

 

 

람다 익스프레션의 캡처 리스트와 STL 활용

이번 시간에는 람다 익스프레션의 캡처 리스트(Capture List) 안에서 this 키워드가 사용되는 경우와, 추가적으로 STL 라이브러리와 람다 익스프레션이 함께 사용되는 예제를 알아봅니다.

 

 

1. 람다 익스프레션과 클래스 멤버 접근

람다 익스프레션의 캡처 리스트에는 외부 변수뿐만 아니라 클래스 객체를 가리키는 this 포인터도 캡처할 수 있습니다.

예제를 위해 Cat 클래스를 만들어 봅니다.

class Cat
{
private:
    int age;

public:
    Cat(int a)
        : age(a)
    {}

    void Speak()
    {
        std::cout << "Meow" << std::endl;
    }

    void Test()
    {
        auto lambda = []()
        {
            std::cout << "Lambda Function" << std::endl;
        };

        lambda();
    }
};

메인 함수에서:

Cat kitty(1);
kitty.Test();

를 실행하면 클래스 내부의 Test() 함수가 호출되고, 내부의 람다 함수도 실행되어 "Lambda Function"이 출력됩니다.

 

 

 

2. 람다에서 클래스 멤버 변수 접근 문제

이번에는 람다 내부에서 클래스 멤버 변수인 age에 접근해 보겠습니다.

auto lambda = []()
{
    std::cout << age;
};

하지만 이렇게 작성하면 컴파일 에러가 발생합니다.

그 이유는 람다 함수의 바디에서는 기본적으로 바깥쪽 클래스의 멤버 변수나 멤버 함수에 직접 접근할 수 없기 때문입니다.

 

 

 

3. this 캡처

이 문제를 해결하기 위해 this를 캡처할 수 있습니다.

auto lambda = [this]()
{
    std::cout << age;
};

this를 캡처하면 람다 내부에서 클래스 멤버 변수와 멤버 함수에 접근할 수 있습니다.

실행하면:

1

이 출력됩니다.

또한 멤버 함수도 호출 가능합니다.

auto lambda = [this]()
{
    Speak();
};

이 경우 람다 내부에서 클래스의 Speak() 함수를 호출할 수 있습니다.

 

 

 

4. this 캡처의 내부 동작

this는 객체 자신을 가리키는 포인터입니다.

따라서:

[this]

는 객체 자체를 복사하는 것이 아니라 객체의 주소(포인터)를 캡처하는 것과 같습니다.

즉 내부적으로는:

Lambda Object

+----------------+
| this pointer   |
+----------------+

        |
        v

      Cat 객체

와 같은 구조로 동작합니다.

 

 

 

5. 자동 캡처와 this

람다에서는 자동 캡처도 가능합니다.

[&]

를 사용하면 컴파일러가 필요한 외부 변수를 자동으로 참조 캡처합니다.

이 경우 클래스 멤버 변수 접근이 필요하면 컴파일러가 자동으로 this를 캡처합니다.

하지만 자동 캡처는 편리한 만큼 예상하지 못한 변수를 캡처할 가능성이 있습니다.

따라서 일반적으로는:

[this]

처럼 명확하게 작성하는 것이 코드의 안정성과 가독성 측면에서 좋습니다.

 

 

 

6. STL과 람다 익스프레션

람다 익스프레션은 STL 알고리즘과 함께 매우 자주 사용됩니다.

예를 들어:

std::vector<int> nums = {1,2,3,4,5,6,7,8,9,10};

이라는 벡터가 있다고 하겠습니다.

여기에 모든 값을 10씩 증가시키는 람다 함수를 만들 수 있습니다.

auto addTen = [](int& n)
{
    n += 10;
};

이 함수는 입력받은 정수 레퍼런스에 10을 더합니다.

예:

int n = 10;

addTen(n);

std::cout << n;

결과:

20

 

 

7. for_each와 람다

STL에는 반복자 범위에 함수를 적용하는 std::for_each 함수가 있습니다.

형태:

std::for_each(first, last, function);

의미:

  • first부터 last까지 순회
  • 각각의 요소에 전달된 함수 적용

예:

std::for_each(
    nums.begin(),
    nums.end(),
    addTen
);

실행하면:

기존:

1 2 3 4 5 6 7 8 9 10

변경 후:

11 12 13 14 15 16 17 18 19 20

이 됩니다.

 

 

8. STL 함수 내부에 람다 직접 전달

람다의 장점 중 하나는 이름 없는 함수(Anonymous Function)를 바로 전달할 수 있다는 점입니다.

기존 방식:

auto addTen = [](int& n)
{
    n += 10;
};

std::for_each(
    nums.begin(),
    nums.end(),
    addTen
);

하지만 더 간단하게:

std::for_each(
    nums.begin(),
    nums.end(),
    [](int& n)
    {
        n += 10;
    }
);

처럼 작성할 수 있습니다.

이 방식은 STL과 람다를 사용할 때 매우 일반적인 C++ 스타일입니다.

 

 

 

핵심 정리

this 캡처

  • 클래스 내부 람다에서 멤버 변수와 함수에 접근하기 위해 사용
  • this는 객체 포인터를 의미
  • [this]는 객체를 복사하는 것이 아니라 포인터를 캡처

 

 

람다와 STL

람다는 STL 알고리즘과 매우 잘 결합됩니다.

대표적인 활용:

  • std::for_each
  • std::sort
  • std::find_if
  • std::transform

등에서 함수 객체 대신 람다를 직접 전달할 수 있습니다.

 

 

이번 시간에는 람다 익스프레션에서 this가 캡처되는 방식과 STL 함수에 람다를 전달하는 기본적인 활용 방법을 알아보았습니다.
특히 현대적인 C++ 코드에서는 STL 알고리즘과 람다 익스프레션을 함께 사용하는 경우가 매우 많기 때문에 익숙해지는 것이 중요합니다.