와와

[ 씹어먹는 C++ ] 함수 객체 본문

개발/C++

[ 씹어먹는 C++ ] 함수 객체

정으주 2024. 10. 12. 21:34

함수 객체

 

Callable

 

()를 붙여서 호출할 수 있는 모든 것

 

 

Std::function

 

모든 Callable들을 보관할 수 있는 객체

#include <functional>
#include <iostream>
#include <string>

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();
}

 

std::function<리턴값 형태(인자값 형태)> 이름 = Callable;

 

와 같은 형태로 템플릿 인자로 전달 받을 함수의 타입을 가진다.

 

 

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

 

멤버 함수를 호출할 때, 구현 상 자신을 호출한 객체를 인자로 암묵적으로 받고 있다.

따라서 아래와 같은 코드를 실행하면 에러가 날 것이고, 해당 클래스의 객체를 인자로 보내주어야 올바르다.

 

잘못된 예시

#include <functional>
#include <iostream>
#include <string>

class A {
  int c;

 public:
  A(int c) : c(c) {}
  int some_func() { std::cout << "내부 데이터 : " << c << std::endl; }
};

int main() {
  A a(5);
  std::function<int()> f1 = a.some_func;
}

 

 

수정한 예시

#include <functional>
#include <iostream>
#include <string>

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;

  f1(a);
  f2(a);
}

 

 

멤버 함수들을 함수 객체로 -mem_fn

멤버 함수를 좀 더 쉽게 함수 객체로 변환하고 싶다면 std::mem_fn를 사용할 수 있다.

std::mem_fn은 멤버 함수 포인터를 받아 해당 멤버 함수를 호출할 수 있는 함수 객체를 생성한다.

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
using std::vector;

int main() {
  vector<int> a(1);
  vector<int> b(2);
  vector<int> c(3);
  vector<int> d(4);

  vector<vector<int>> container;
  container.push_back(b);
  container.push_back(d);
  container.push_back(a);
  container.push_back(c);

  vector<int> size_vec(4);
  std::transform(container.begin(), container.end(), size_vec.begin(),
            std::mem_fn(&vector<int>::size));
  for (auto itr = size_vec.begin(); itr != size_vec.end(); ++itr) {
    std::cout << "벡터 크기 :: " << *itr << std::endl;
  }
}

 

여기서 사용된 std::transform 함수는 <algorithm> 라이브러리에 있는 함수로, 각 원소들에 대해 인자로 전달된 함수를 실행시킨 다음 그 결과를 전달된 컨테이너에 넣어준다.

std::mem_fn(&vector<int>::size) 는 vector<int> 객체를 받아 그 객체의 size() 멤버 함수를 호출하는 함수 객체를 생성한다. 이 함수 객체는 transform 함수의 요구사항에 맞게 동작하여, 각 vector의 size를 구해 새로운 vector에 저장할 수 있게 된다.

 

std::bind

std::bind를 사용하면 기존 함수의 일부 인자를 고정하거나 인자의 순서를 바꾸어 새로운 함수 객체를 만들 수 있다.

#include <functional>
#include <iostream>

void add(int x, int y) {
  std::cout << x << " + " << y << " = " << x + y << std::endl;
}

void subtract(int x, int y) {
  std::cout << x << " - " << y << " = " << x - y << std::endl;
}

int main() {
  auto add_with_2 = std::bind(add, 2, std::placeholders::_1);
  add_with_2(3);

  // 두 번째 인자는 무시된다.
  add_with_2(3, 4);

  auto subtract_from_2 = std::bind(subtract, std::placeholders::_1, 2);
  auto negate =
      std::bind(subtract, std::placeholders::_2, std::placeholders::_1);

  subtract_from_2(3);  // 3 - 2 를 계산한다.
  negate(4, 2);        // 2 - 4 를 계산한다
}

 

 

bind는 기본적으로 인자를 값으로 복사한다. 따라서 레퍼런스로 인자를 전달하고 싶다면 std::ref나 std::cref를 사용해야 한다.