bdfgdfg
[C++ 11] std::function, std::bind 본문
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(30, 20);
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(20, 30); // 에러
|
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(int, int)> intFunc = FuncObject();
strFunc("Hello ", "World!");
intFunc(10, 20);
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, 10, 20);
auto f2 = std::bind(Sum, 30, 40);
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, 10, std::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 사이트를 참고했습니다.
'게임프로그래밍 > C++' 카테고리의 다른 글
[C++ 11] std::atomic, SpinLock (1) | 2022.02.07 |
---|---|
[C++ 11] 스마트 포인터(Smart Pointer) - 2 (0) | 2022.01.26 |
[C++ 11] 스마트 포인터(Smart Pointer) - 1 (0) | 2022.01.26 |
[C++ 11] 오른값 참조 및 이동 의미(move semantic) (0) | 2022.01.25 |
[C++ 11] 람다(lambda) 식 (0) | 2022.01.24 |