std::function, bind

하루공부·2024년 1월 20일
0

C++

목록 보기
12/25
post-thumbnail

C++ 아이콘 제작자: Darius Dan - Flaticon


std::function

  • C++에서 호출 가능한 모든 것을 Callable이라 한다
    ==> 함수가 아님에도 ()로 호출하는 것도 포함.

  • 모든 Callable들을 객체의 형태로 보관할 수 있는 std::function 클래스.

    • function 객체는 템플릿 인자로 전달 받을 함수의 타입을 갖는다
    • 여기서 함수의 타입이라 하면, 리턴값과 함수의 인자들이다.
int some_func1(const std::string& a) {
std::cout << "Func1 호출! " << a << std::endl;
return 0; }

struct S {
void operator()(char c) { std::cout << "Func2 호출! " << c << std::endl; } };

int main() {
std::function<int(const std::string&)> f1 = some_func1;
std::function<void(char)> f2 = S();
std::function<void()> f3 = []() { std::cout << "Func3 호출! " << std::endl; } } -> 람다함수

f1("hello")
f2('c')
f3()
  • f1의 경우 some_fucn1은 int를 리턴하며 const std::string&을 받기에 <int(const std::string&)>을 전달하여 정의했다.
  • f2인 경우 단순히 함수 객체를 넘겨주고 인자로 char를 받고 void를 리턴하니 <void<char)> 표현
  • f3은 람다 함수로 리턴값, 인자값 모두 void이므로 <void()>으로 정의

멤버 함수를 가지는 std::function

  • 멤버 함수를 보관하려면 조금 이야기가 달라진다.

    왜냐면 멤버 함수 내에서 this 의 경우 자신을 호출한 객체를 의미하는데
    그냥 function 에 넣게 된다면 this 가 무엇인지 알 수 없는 문제가 발생

    그냥 함수를 저정하기에 어떤 객체에 대한지 정보가 없음.

==> 우짜지? 객체에 대한 추가적인 정보를 전달해야 한다.

class A {
int c;
public:
A(int c) : c(c) {}
int some_func() {
std::cout << "비상수 함수: " << ++c << std::endl;
return c;
}
int some_const_function() const {
std::cout << "상수 함수: " << c << std::endl;
return c;
}
static void st() {}
};
int main() {
A a(5);
std::function<int(A&)> f1 = &A::some_func;
std::function<int(const A&)> f2 = &A::some_const_function; ==> 상수 함수라 인자에 const를 붙였음
f1(a);
f2(a);
}

앞서 다르게 함수 이름 앞에 &가 붙어있다 A::은 클래스의 멤버 함수를 나타내기 위해 사용했고
이는 C++의 규칙인데 멤버 함수는 이름이 주소값으로 암시작 변환이 일어나지만
멤버함수의 경우 그렇지 않아서 명시적으로 &를 사용해 주소값을 전달한다.


  • 객체의 정보는?

    인자에 해당 클래스의 레퍼런스를 넘기는데 해당 fucntion객체를 호출할 때
    해당 클래스 객체를 전달한다.
    ==> 객체를 참조할 수 있게되어 해당 객체에서 멤버 함수가 작동한다.

    만약 함수가 상수 함수라면 상수 형태의 인자를 받으면 되고 아니라면 단순히 객체를 참조할 값을 받는다.


mem_fn

  • 전달 받은 멤버 함수를 function객체로 만들어준다.(functional 헤더 파일을 추가해야한다.)

    멤버 함수의 경우 해당객체.함수이름(); 또는 해당객체->함수이름(); 이렇게 호출한다(이는 c++ 규칙)
    어떠한 함수의 표현식이 f_name(*func) 인데 여기에 멤버 함수 이름이 들어가면 컴파일 에러가 난다
    ==> c++ 규칙을 어김

    그래서 멤버 함수를 function객체로 만들어 객체() 형태로 함수를 호출할 수 있게 만든다.

ex) std::function<int(const vector<int>&)> sz_func = &vector<int>::size;

==> 위 예시의 로직을 좀 더 편하게 mem_fn(&vector<int>::size) 만들 수 있다.


  • 그런데 사실 잘 사용안한다. ==> 람다 함수가 있기 때문이다.

    [](const auto&v){ return v.size()}



std::bind

  • bind 함수는 이름 그대로 원래 함수에 특정 인자를 붙여(bind)준다.
 auto add_with_2 = std::bind(add, 2, std::placeholders::_1);
 add_with_2(3); // 첫 번쨰 인자로는 2가 들어가고, 두번쨰 인자로는 함수 객체 인자인 3이 들어간다.
  • 위 예시의 경우 add 라는 함수에 첫 번째 인자로 2 를 bind 시켜주고
    두 번째 인자로는 새롭게 만들어진 함수 객체의 첫 번째 인자를 전달한다.
  • 혹여나 여러개의 인자를 함수 객체로 전달하면 뒤에 있는 인자들은 무시된다.

auto subtract2 = std::bind(subtract, std::placeholders::_2, std::placeholders::_1);

첫 번째 인자와 두 번째 인자의 순서를 바꿔서 subtract함수를 호출
즉 subtract2(3, 5) 를 호출할 경우 실제로는 subtract(5, 3)가 호출되는 것임


  • placeholders는 인자라는 뜻 ::_1 _2는 만들어진 함수 객체의 전달 받는 인자의 인덱스( _3은 3번째 인자인거지)
  • 만약 _4만을 bind하는데 사용했다면 인자를 4개 전달을 해줘야함

    ex) std::bind(add, 2, std::placeholders_4) 이렇게 작성하면 bind로 새로 정의된 함수의 첫번째 인자는 2로 고정
    그리고 해당 함수를 인자를 4개 넘겨주어 4번째 인자가 add에 2번째 인자로 들어간다


  • 한 가지 주의할 점은 레퍼런스를 인자로 받는 함수들의 경우
truct S {
int data;
S(int data) : data(data) { std::cout << "일반 생성자 호출!" << std::endl; }
S(const S& s) {
std::cout << "복사 생성자 호출!" << std::endl;
data = s.data; }
S(S&& s) {
std::cout << "이동 생성자 호출!" << std::endl;
data = s.data;} };

void do_something(S& s1, const S& s2) { s1.data = s2.data + 3; }
int main() {
S s1(1), s2(2);
std::cout << "Before : " << s1.data << std::endl;   
auto do_something_with_s1 = std::bind(do_something, s1, std::placeholders::_1);
 // s1 이 그대로 전달된 것이 아니라 s1 의 복사본이 전달됨! ==> 복사 생성자 호출됨
do_something_with_s1(s2);
std::cout << "After :: " << s1.data << std::endl;
}

bind 함수로 s1이 복사되어 전달된다.
따라서 레퍼런스를 명시적으로 전달해야함
std::bind(do_something, std::ref(s1), std::placeholders::_1);


공부한 내용 복습

개인 공부 기록용 블로그입니다.
틀린 부분 있으다면 지적해주시면 감사하겠습니다!!

0개의 댓글