본문 바로가기

C++

c++ 17 ( 람다 함수 )

람다함수 (= 익명 함수)

: 변수형 함수 

 

#include <functional>

std::function을 사용하기 위한 헤더파일

이 헤더파일을 포함해야 std::function을 사용할 수 있다.

 

람다를 사용하기 위해 꼭 사용할 필요는 없지만

해두지 않으면 여러모로 문제가 생긴다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <functional>


int main() {
    std::function<void()> foo = [=]() { 
        printf("Hello World");
    }; // 파란색 부분이 람다 함수
    // std::function<void()> foo = 부분은 람다 함수를 받아서
        std function으로 변환돼서 변수 형태로 함수가 만들어지게 된 것


     
foo();
     return 0;


}

 

 

람다 함수의 구조

[ 캡쳐 ] ( 파라미터 ... ) -> 리턴 { ... }

 

[ 캡쳐 ] : 람다함수 외부의 값을 캡쳐하는 곳
( 파라미터 ... ) : 람다함수의 파라미터

 -> 리턴 : 람다함수의 리턴값을 정의

{ ... } : 함수의 몸통

 

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <functional>


int main() {
    
    int myvalue = 100;
    
    std::function<void()> foo = [=]() {
        printf("Hello World %d", myvalue); // 안된다 
    };
    
    foo();
    return 0;
}

람다 함수 바깥에 있는 값을 안쪽에서 쓸 수가 없다.

이것을 가능하게 하는 것이 캡쳐 부분인데

대부분의 경우 [=]을 써서 값으로 캡쳐하게 된다.

 

[&]는 레퍼런스로 캡쳐하는 경우

대부분의 경우 캡쳐한 순간에 값이 안에서 쓰는 경우가 일반적

 

 

 

 

std::function<T>

람다함수는 std::function의 변수형에 지정될 수 있다.

 

이 지정된 변수는 함수의 리턴 타입과 파라미터 타입에 따라 정해지게 되는데

예를 들어 리턴타입이 bool이고 파라미터를 int, float로

두 개를 받는다면 템플릿 타입은 다음과 같다.

std::function<bool (int, float)> someFunction;

 

 

std::function<void()> foo = [=]() {
        printf("Hello World %d", myvalue); 

void() 는 함수 타입을 지정해주는 곳, void면 파라미터가 없는 함수타입

 

int, float의 경우

std::function<void(int, float)> foo = [=](int value1, float value2) {

이런식으로 파라미터의 값을 받아줘야한다.

 

보통의 경우 너무 길어지기 때문에

std::function<void(int, float)>을 auto 라는 키워드로 대체한다.

타입이 굉장히 길어질 때 한 번에 만들어서 assign 할거면 auto라는 타입을 사용 

 

auto를 사용하더라도 타입의 원형이 std::funcion인지는 알아야한다.

 

 

 

 

 

std::bind

: 함수 그자체를 끌어오는 것

일반적인 함수를 변수형태의 어떤 함수로 만들어 줄 수 있다.

 

람다함수는 새로운 변수형 함수를 만든다고 한다면

std::bind는 이미 존재하는 함수를 변수 형태로 만들어

std::function 클래스 인스턴스로 만들어준다.

 

이 std::bind를 활용하면 이미 존재하는 함수를

변수 형태로 취급할 수 있게 된다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <functional>
#include <string>


void foo() {
    printf("Hello World\n");
}


int sum(int value1, int value2) {
    return value1 + value2;
}


int main() {
    
    std::function<void()> local_foo = std::bind(foo);
    // 함수형 변수, foo()함수를 가지고 local_foo 변수를 만들었다.
    // std::function<void()> 대신 auto 사용 가능


    local_foo();
    
    auto local_sum = std::bind(sum, std::placeholders::_1, std::placeholders::_2);
    // sum 함수에 파라미터가 있기 때문에 std::placeholders::_1, std::placeholders::_2 써준다.
    
    int result = local_sum(10, 20);
    printf("result: %d\n", result);
   
    return 0;
}

 

 

std::bind는 클래스 내부에 있는 함수를 바인딩하기 위해서는 몇 가지 절차가 필요하다.

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <functional>
#include <string>


class Marine{
public:
    void attackTo(const char* target) {
        printf("마린은 %s를 공격했다.\n", target);
    }
};


int main() {
    
    Marine* m = new Marine();
    
    // 클래스 Marine에 있는 attackTo를 바인딩 하고 싶은 경우
    auto attack = std::bind(&Marine::attackTo, m, std::placeholders::_1);

    attack("히드라");
    // attack이라는 함수는 자동적으로 Marine* m의 m이 이 클래스 인스턴스의 attackTo라는
        함수를 히드라 라는 파라미터를 갖고 실행하게 된다.
    
    return 0;
}

 

std::bind를 쓰는 경우

대부분의 경우에 Callback 함수에서 이용이 된다.

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <functional>
#include <string>


int main() {
    
    int some_value = 50;
    
    auto foo = [=](int a, int b) ->int {
        printf("Hello World %d\n", some_value);
        return a + b;
    }; // 함수인데도 {}안에서 마치 변수처럼 선언이 되기 때문에 ; 붙이기
    
    int value1 = 100;
    int value2 = 200;
    
    int result = foo(100, 200);
    
    printf("%d", result);
    
    return 0;
}


// 위에서 아래로 실행되는 구조이기 때문에 some_value가 나타나지 않는 시점에 선언하게 되면 some_value를 캡처할 수 없어 오류
    캡처되는 안쪽의 값은 항상 선언되는 것 보다 위쪽에 있어야한다.