함수 객체

·2022년 6월 28일
0

cpp_study

목록 보기
21/25

Callable

Callable: 호출할 수 있는 모든 것

람다 함수, 함수 객체 등 역시 callable이라고 할 수 있음.

std::function

callable들을 객체의 형태로 보관할 수 있는 std::function이라는 클래스를 제공함.

#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<리턴타입(인자타입)> 함수이름 = 진짜함수이름;
  std::function<void(char)> f2 = S();
  std::function<void()> f3 = []() { std::cout << "Func3 호출! " << std::endl; };
  f1("hello");
  f2('c');
  f3();
}

멤버 함수를 가지는 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;
    return c;
  }
  int some_const_function() const {
    std::cout << "상수 함수: " << c << std::endl; return c;
  }
  static void st() {} 
};

int main() { 
  A a(5);
  // 원래 인자에 추가적으로 개게를 받는 인자를 전달
  // 상수 함수: 상수 형태로 인자를 받아야 함, 상수 함수가 아니면 단순히 A&로 받으면 됨.
  std::function<int(A&)> f1 = &A::some_func;
  std::function<int(const A&)> f2 = &A::some_const_function;
  f1(a);
  f2(a); 
}

이때 const 함수는 이 함수 안에서는 어떤 변수도 바꿀 수 없음(mutable은 예외)를 뜻한다.
멤버 함수들의 경우 암시적 변환이 발생하지 않으므로 & 연산자를 통해 명시적으로 주소값을 전달해줘야 함..

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

vector들을 가지는 vector가 있을 때, 각각의 vector들의 크기들을 벡터로 만들어주는 코드를 생각해 보자.

#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(),
  &vector<int>::size); // 에러 코드
  // std::function<size_t(const vector<int>&)> sz_func = &vector<int>::size;
  
  transform(container.begin(), container.end(), size_vec.begin(),
std::mem_fn(&vector<int>::size)); // mem_fn으로 사용 가능
  
  for (auto itr = size_vec.begin(); itr != size_vec.end(); ++itr) {
    std::cout << "벡터 크기 :: " << *itr << std::endl; 
  }
}

function 객체를 리턴해버리는 함수를 추가함
mem_fn은 이름 그대로, 전달된 멤버 함수를 function 객체로 만들어서 리턴해줌.

std::bind

함수 객체 생성 시에 인자를 특정한 것으로 지정할 수 있음.

bind 함수는 원래 함수에 특정 인자를 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 인자로 특정 객체를 지정하고 싶다면, 아래와 같이 직접 레퍼런스를 명시적으로 전달해줘야 함.
(만약 명시적으로 레퍼런스를 전달하지 않는다면, 복사 생성자가 호출됨)

#include <functional>
#include <iostream>

struct 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;
  // s1 이그대로전달된것이아니라s1 의복사본이전달됨! 
   auto do_something_with_s1 =
       std::bind(do_something, std::ref(s1), std::placeholders::_1);
   do_something_with_s1(s2);
   std::cout << "After :: " << s1.data << std::endl;
}

ref 함수는 전달받은 인자를 복사 가능한 레퍼런스로 변환해 주어 s1의 레퍼런스가 잘 전달될 수 있게 함(bind 함수 안으로).

const 레퍼런스의 경우 cref 함수를 호출하면 됨.

profile
이것저것 개발하는 것 좋아하지만 서버 개발이 제일 좋더라구요..

0개의 댓글