[Modern C++] 14. Callable

윤정민·2023년 8월 1일
0

C++

목록 보기
35/46

1. Callable

  • 호출 할 수 있는 모든 것을 의미
  • C++에서는 ()를 붙여서 호출할 수 있는 모든 것을 의미

1.1. callable 구현

  • 연산자 ()

    • 예제코드
    #include <iostream>
    
    struct S {
      void operator()(int a, int b) { std::cout << "a + b = " << a + b << std::endl; }
    };
    
    int main() {
      S some_obj;
    
      some_obj(3, 5);
    }
    • 실행 결과
    a + b = 8
  • 람다함수

    • 예제 코드
    #include <iostream>
    
    int main() {
      auto f = [](int a, int b) { std::cout << "a + b = " << a + b << std::endl; };
      f(3, 5);
    }
    • 실행결과
    a + b = 8

2. std::function

  • Callable들을 객체의 형태로 보관할 수 있는 클래스 제공
  • 함수 뿐만 아니라 모든 Callable들을 보관할 수 있는 객체

2.1. 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<void(char)> f2 = S();
  std::function<void()> f3 = []() { std::cout << "Func3 호출! " << std::endl; };

  f1("hello");
  f2('c');
  f3();
}
  • function객체는 템플릿 인자로 전달 받을 함수의 타입을 가짐
    • 함수의 타입: 리턴값과 함수의 인자들
  • Functor인 클래스 S의 객체의 경우 단순히 S의 객체를 전달해도 이를 마치 함수 인양 받게 됨
    • operator()가 인자로 char을 받고 리턴타입이 void이므로 std::function<void(char)>의 꼴로 표현할 수 있게 됨

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

  • 멤버 함수 내에서 this의 경우 자신을 호출한 객체를 의미하기 때문에, 만일 멤버함수를 그냥 function에 넣게 된다면 this가 무엇인지 알 수 없는 문제가 발생
  • 멤버 함수들은 구현 상 자신을 호출한 객체를 인자로 암묵적으로 받기 때문에 자신을 받는 인자를 전달해주면 위 문제 해결 가능
  • 멤버 함수들의 경우 암시적 변환이 발생하지 않으므로 &연산자를 통해 명시적으로 주소값을 전달해야 함
  • 예시 코드
#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; //인자로 자신인 A class 전달
  std::function<int(const A&)> f2 = &A::some_const_function;

  f1(a);
  f2(a);
}

3. mem_fn

  • 전달된 멤버 함수를 function객체로 만들어 리턴해줌
#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(a);
  container.push_back(b);
  container.push_back(c);
  container.push_back(d);

  vector<int> size_vec(4);
  transform(container.begin(), container.end(), size_vec.begin(),
            std::mem_fn(&vector<int>::size)); //매개함수로 function객체를 넘겨주기 위해 mem_fn사용
  for (auto itr = size_vec.begin(); itr != size_vec.end(); ++itr) {
    std::cout << "벡터 크기 :: " << *itr << std::endl;
  }
}

4. std::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 를 계산한다
    }
  • auto add_with_2 = std::bind(add, 2, std::placeholders::_1);

    • add라는 함수에 첫 번째 인자로 2를 bind시켜주고, 두 번째 인자로는 새롭게 만들어진 함수 객체의 첫 번째 인자를 전달
  • add_with_2(3);

    • 원래 add함수의 첫 번째 인자로는 2가 들어가게 되고, 두 번째 인자로 add_with_2의 첫번째 인자인 3이 들어감
  • add_with_2(3, 4);

    • 인자를 여러개 전달하더라도 뒤에 것들은 무시 됨
  • auto negate = std::bind(subtract, std::placeholders::_2, std::placeholders::_1);

    • 인자의 순서를 바꿀 수도 있음
  • bind함수로 인자가 복사되어 전달하기 때문에 const가 아니라면 레퍼런스를 전달해야 됨을 주의하자

profile
그냥 하자

0개의 댓글