C++ 아이콘 제작자: Darius Dan - Flaticon
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()>으로 정의
왜냐면 멤버 함수 내에서 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객체를 호출할 때
해당 클래스 객체를 전달한다.
==> 객체를 참조할 수 있게되어 해당 객체에서 멤버 함수가 작동한다.만약 함수가 상수 함수라면 상수 형태의 인자를 받으면 되고 아니라면 단순히 객체를 참조할 값을 받는다.
멤버 함수의 경우 해당객체.함수이름(); 또는 해당객체->함수이름(); 이렇게 호출한다(이는 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()}
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);
개인 공부 기록용 블로그입니다.
틀린 부분 있으다면 지적해주시면 감사하겠습니다!!