C++에서의 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);
}
some_obj는 클래스 S의 객체로, 함수는 아님.
하지만 마치 함수처럼 ()를 이용해서 호출 가능 = callable
#include <iostream>
int main() {
auto f = [](int a, int b) { std::cout << "a + b = " << a + b << std::endl; };
f(3, 5);
}
f() 역시 일반적인 함수의 꼴은 아니지만, ()를 통해서 호출 가능 = callable
std::function ?
Callable 들을 객체의 형태로 보관할 수 있는 클래스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() {
// function 객체를 정의하는 부분
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 객체는 템플릿 인자로 전달 받을 함수의 타입을 갖게 됨.
-> 여기서 함수의 타입이란, 리턴값과 함수의 인자들을 의미.
예를 들어, some_func1 의 경우 int 를 리턴하며, 인자로 const string& 을 받기 때문에 위와 같이 std::function<int(const string&)> 의 형태로 정의
Functor 인 클래스 S 의 객체의 경우 단순히 S 의 객체를 전달해도 이를 마치 함수 인양 받게 됨. S 의 경우 operator() 가 인자로 char 을 받고 리턴타입이 void 이므로 std::function<void<char)> 의 꼴로 표현할 수 있음
람다 함수의 경우 마찬가지로 리턴값이 없고 인자를 받지 않기 때문에 std::function<void()> 로 정의
멤버 함수 내에서 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);
// 따라서 이와 같이 인자에 추가적으로 객체를 받는 인자 전달
// 상수함수가 아닌 경우 - A& 형태
std::function<int(A&)> f1 = &A::some_func;
// 상수함수인 경우 - const A& 형태
std::function<int(const A&)> f2 = &A::some_const_function;
// 아래와 같이 호출하고자 하는 객체를 인자로 전달해주면,
// 마치 해당 객체의 멤버 함수를 호출한 것과 같은 효과를 낼 수 있음.
f1(a);
f2(a);
}

멤버 함수가 아닌 모든 함수들의 경우 함수의 이름이 함수의 주소값으로 암시적 변환이 일어나지만, 멤버 함수들의 경우 암시적 변환이 발생하지 않으므로
&연산자를 통해 명시적으로 주소값을 전달해줘야 함.
mem_fn 함수는 이름 그대로, 전달된 멤버 함수를 function 객체로 예쁘게 만들어서 리턴해줌.
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(a);
container.push_back(b);
container.push_back(c);
container.push_back(d);
vector<int> size_vec(4);
// std::mem_fn 사용!
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;
}
}

주의사항
참고로 mem_fn 은 그리 자주 쓰이지는 않는데, 람다 함수로도 동일한 작업을 수행할 수 있기 때문. 위 코드의 경우mem_fn(&vector<int>::size)대신에[](const auto& v){ return v.size()}를 전달해도 동일한 작업을 수행함.
mem_fn 을 사용하기 위해서는<functional>헤더를 추가해야 하지만 람다함수는 그냥 쓸 수 있으니 좀 더 편리한 면이 있음. 물론, 코드 길이 면에서는 mem_fn 을 사용하는 것이 좀더 깔끔한 편임.
함수 객체 생성 시에 인자를 특정한 것으로 지정해줌.
#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);
// bind !!!!
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 함수는 이름 그대로 원래 함수에 특정 인자를 붙여(bind)줌.
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);
위처럼 인자를 여러개 전달하더라도 뒤에 것들은 무시됨.
레퍼런스를 인자로 받는 함수의 경우 bind 사용 시 주의해야 함. :
bind 함수로 인자가 복사 되서 전달되기 때문!
따라서 이를 해결 하기 위해서는 명시적으로 s1(변수) 의 레퍼런스를 전달해줘야 함.
// s1 이 그대로 전달된 것이 아니라 s1 의 복사본이 전달됨!
auto do_something_with_s1 =
std::bind(do_something, std::ref(s1), std::placeholders::_1);
do_something_with_s1(s2);
ref 함수를 사용함으로써, 전달받은 인자를 복사 가능한 레퍼런스로 변환해줌.
const 레퍼런스의 경우, cref함수를 호출하면 됨.