std::fuction

MwG·2025년 3월 27일

C++

목록 보기
11/14

std::function은 단순 함수뿐만 아니라 operator(), 람다, 멤버 함수, std::bind 등을 포함한 모든 callable 객체를 저장하고 호출할 수 있는 다형적인 함수 래퍼 클래스입니다.

일반적인 함수

int some_function(const std::string& s)
{
	std::cout << s << "함수 호출!" << std::endl;
	return 0;
}

struct S
{
	void operator()(const std::string& s)
	{
		std::cout << "구조체 오퍼레이터 함수 호출!" << std::endl;
	}
};

int main() 
{


	std::function<int(const std::string& s)> f1 = some_function;
	std::function<void(const std::string& s)> f2 = [](const std::string& s) -> void { std::cout << "람다 함수 호출" << s << std::endl;};
	std::function<void(const std::string& s)> f3 = S();

	f1("sss");
	f2("sss");
	f3("sss");

	return 0;
	
}

템플릿 인자로 함수의 타입(반환, 인자)를 받아 함수 객체를 보관하는 역할

멤버함수

멤버함수의 경우엔?

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

내부 함수의 c가 어떤 객체의 c를 참조하는지 알 수 없기에 오류가 발생한다.
즉, 멤버 함수는 객체 없이 단독 호출이 불가능하기 때문에, std::function에 넣을 때는 객체 참조를 인자로 받는 형식으로 감싸야 합니다.

그렇기에

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

	int some_const_function() const { std::cout << "내부데이터, const: " << c << std::endl; }
};


int main() {
	A a(5);
		// 비-const 멤버 함수 포인터
	std::function<int(A&)> f1 = &A::some_func;
	f1(a);

	// const 멤버 함수 포인터
	std::function<int(const A&)> f2 = &A::some_const_func;
	f2(a);

}

함수 이름만으로는 멤버 함수 포인터로 암시적 변환되지 않기 때문에 &A::some_func처럼 명시적 주소 연산자가 필요합니다.

std::mem_fn

멤버 함수 포인터를 함수 객체로 변환해주는 도우미 함수입니다. 객체를 인자로 넘기기만 하면 됩니다.

#include <iostream>
#include <functional>
#include <vector>

class A {
public:
	void print() const { std::cout << "mem_fn 예제" << std::endl; }
};

int main() {
	A a;
	auto f = std::mem_fn(&A::print);
	f(a); // mem_fn을 이용한 멤버 함수 호출

	std::vector<A> vec(3);
	for (const auto& item : vec) f(item); // STL 컨테이너에도 적용 가능
}

std::Bind

std::bind는 기존 함수나 멤버 함수에 인자를 고정(bind) 시켜서 새로운 호출 가능한 객체를 만듭니다.

#include <iostream>
#include <functional>

void greet(const std::string& name, int times) {
	for (int i = 0; i < times; ++i)
		std::cout << "Hello, " << name << "!\n";
}

int main() {
	auto say_hello = std::bind(greet, "Alice", 3);
	say_hello(); // Hello, Alice! x3

	auto say_n_times = std::bind(greet, std::placeholders::_1, 2);
	say_n_times("Bob"); // Hello, Bob! x2
}

std::placeholders::_1 여러 숫자가 있는데 인자의 순서를 나타낸다.

auto g = std::bind(show, std::placeholders::_2, std::placeholders::_1, 300);

이런식으로 할 경우 g(2,3)이라고 할 경우 -> (3,2)의 순서로 나타난다.

레퍼런스의 경우 명시적으로 std::ref(s1) 전달해주지 않으면 데이터가 변경되지 않을 수 있는데 인자가 복사되어 전달되기 때문이다.

#include <iostream>
#include <functional>

void modify(std::string& s) {
	s += " world";
}

int main() {
	std::string s = "hello";

	// 값 복사 - 원본 변경 안 됨
	auto f1 = std::bind(modify, s);
	f1();
	std::cout << s << std::endl; // 출력: hello

	// 참조로 전달
	auto f2 = std::bind(modify, std::ref(s));
	f2();
	std::cout << s << std::endl; // 출력: hello world
}

std::bind는 인자를 기본적으로 값으로 복사하므로, 원본 객체를 수정하고 싶다면 반드시 std::ref()로 참조 전달해야 합니다.

멤버 함수 바인딩의 경우에도


class A {
public:
	void hello(const std::string& msg) {
		std::cout << "A says: " << msg << "\n";
	}
};

A a;
auto f = std::bind(&A::hello, &a, "Hi");
f(); // A says: Hi
void A::hello(A* this, const std::string& msg);

내부적으론 이런형태이기 때문에 꼭 주소를 명시를 해주어야 한다.

MS functional 헤더에 대한 자세한 참고자료

0개의 댓글