bdfgdfg

[C++ 11] std::function, std::bind 본문

게임프로그래밍/C++

[C++ 11] std::function, std::bind

marmelo12 2022. 2. 15. 19:21
반응형

std::function

std::function은 호출할 수 있는 모든 것을 Callable이라 하며, C++에서는 ()를 붙여 호출할 수 있는 모든것으로 정의한다.

위 경우 함수만을 떠올릴 수 있지만, C++에서는 Callable의 방법은 다양하다.

1
2
3
4
5
6
7
8
9
10
11
12
struct FuncObject
{
    int operator()(int a, int b) { return a + b; }
};
 
int main()
{
    FuncObject a;
    std::cout << a(3020);
 
    return 0;
}
cs

실행결과는 50이 출력 된다.

여기서 FuncObject 타입으로 생성된 변수 a는 함수가 아니다. 하지만 연산자 오버로딩을 통해 마치 함수인것처럼 호출이 가능하다.

 -> 이를 함수객체라 한다.

 

이렇게 C++에서는 위와 같은 Callable한 모든 것들을 객체의 형태로 보관할 수 있는 기능이 바로 std::function.

 -> 람다도 가능.

 -> C 스타일의 함수 포인터의 경우에는 함수만 저장 가능했다.

1
2
3
4
5
FuncObject a;
int (*ptrFunc)(int a, int b);
    
ptrFunc = a(2030); // 에러

cs

이제 std::function의 간단한 사용법을 보자. 우선 사용을 위해서는 functional 헤더를 추가해줘야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
std::string Sum(const std::string& lhs, const std::string& rhs)
{
    std::cout << "일반 함수 호출" << std::endl;
    return lhs + rhs;
}
 
struct FuncObject
{
    int operator()(int a, int b) 
    { 
        std::cout << "함수객체 호출" << std::endl;
        return a + b; 
    }
};
 
int main()
{
    std::function<std::string(const std::string&const std::string&)> strFunc = Sum;
    std::function<int(intint)> intFunc = FuncObject(); 
 
    strFunc("Hello ""World!");
    intFunc(1020);
 
    return 0;
}
cs

실행 시

위와 같이 잘 동작한다.

std::function은 템플릿 인자로 등록할 Callable의 반환형을 넣어주면 된다.

예로들어 int형을 반환하고 매개변수로 int형이 3개라면, std::function<int(int,int,int)> 인 것.

만약 반환형이 void고 매개변수가 없다면 std::function<void()>.

 

이렇게 C++11에서 나온 std::function은 C 스타일의 함수포인터보다 좀 더 다양한 Callable들을 저장할 수 있다.

 

당연하지만, std::function은 클래스(구조체)의 멤버함수도 저장이 가능하다. (이건 함수포인터도 가능하다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Test
{
public:
    void Output() { std::cout << "멤버함수 호출!" << std::endl; }
};
 
int main()
{
    void (Test::*pFunc)() = &Test::Output;
 
    pFunc(); // 에러! this가 없기때문.
 
    Test t1;
    (t1.*pFunc)(); // 호출하기가 까다롭다.
 
    return 0;
}
cs

기존의 C 스타일의 함수포인터는 위와 같이 저장이 가능하지만, 호출하는데 있어 조금 까다롭다.

 

std::function은 좀 더 간단하게 사용이 가능하다. (물론 this는 당연히 필요하다)

1
2
3
4
5
// 첫번째 인자로 this. 자기자신을 넘긴다.
Test t;
std::function<void(Test&)> memberFunc = &Test::Output;
 
memberFunc(t);
cs

왜 자기자신(this)을 넘겨야 할까. 물론 this를 알아야 멤버함수를 호출할 수 있는건 당연한거지만, 인자로 넘기는것에는 익숙치 않을 수 있다. 하지만 사실은 객체(this)를 통해 멤버함수를 호출할 때 호출한 자기자신(this)을 암묵적으로 인자로 넘기고 있었다.

 -> (파이썬의 인자 self와 같음)

 -> 참고로 멤버함수의 경우에는 함수 이름이 암시적으로 주소값 변환이 일어나지 않기에 &연산자를 붙여야 한다.

 

기본적인 사용방법은 위와 같다.

 

std::bind

std::bind또한 C++11에서 추가된 기능이며, 주로 std::function과 함께 많이 사용된다.

 -> 헤더도 functional안에 존재한다.

std::bind는 간단히 말하면 Callable한 객체(함수)의 인자로 넘길 값을 고정(바인딩)시키는 것.

 -> 반환은 std::function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int Sum(int a, int b)
{
    return a + b;
}
 
int main()
{
    // 함수명,인자...
    // function의 템플릿 인자로 매개변수목록을 넣어줄필요가 없다.
    std::function<int()> f1 = std::bind(Sum, 1020);
    auto f2 = std::bind(Sum, 3040);
 
    std::cout << f1() << std::endl;
    std::cout << f2() << std::endl;
 
    return 0;
}
cs

실행결과.

그런데, 어떠한 인자값들은 고정시키되, 특정한 인자값은 내가 직접 넘겨주고 싶을 때 사용할 수 있는게

std::placeholder. (참고로 29개가 최대. 애초에 이렇게 많이 매개변수를 받는 함수가 이상한 것.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
    // 함수명,인자...
 
    std::function<int(int)> f1 = std::bind(Sum, 10std::placeholders::_1);
    std::function<int(int)> f2 = std::bind(Sum, std::placeholders::_1, 40);
    auto f3 = std::bind(Sum, std::placeholders::_1, std::placeholders::_2);
 
    std::cout << f1(20<< std::endl;
    std::cout << f2(40<< std::endl;
    std::cout << f3(70,80<< std::endl;
 
    return 0;
}
cs

std::placeholder를 통해 인자를 직접 넣어주는 경우. function템플릿 인자에도 해당 인자를 받기 위해 자료형을 넣어줘야 한다. 

 -> 이것은 placeholder의 수에 맞춰 넣어준다.

또한 placeholder는 _1부터 시작해서 순차적으로 사용을 해야하며 _1을 사용하지 않았는데 _3을 쓰면

 

위와같이 에러를 내뱉는다.

 

 

https://modoocode.com/254 사이트를 참고했습니다.

반응형
Comments