관리 메뉴

기억을 위한 기록들

[CPP]std::function에 관하여 본문

C & CPP

[CPP]std::function에 관하여

에드윈H 2022. 2. 2. 20:16

 

<functional> 헤더 파일에 정의된 std::function 템플릿을 이용하면 

1. 함수를 가리키는 타입(함수 포인터)

2. 함수 객체

3. 람다 표현식

세개를 비롯하여 호출 가능한 모든 대상을 가리키는 타입을 생성할 수 있다.

 

std::function은 다형성 함수 래퍼(polymorphic function wrapper)라고도 불리며, 함수 포인터로도 사용할 수도 있고,

 콜백을 구현하는 함수를 나타내는 매개변수와 사용할 수도 있다.

 

 

std::function<R(ArgTypes...)>

R은 리턴타입이고, ArgTypes는 각각을 콤마로 구분한 매개변수의 타입 목록이다.

 

 

예제 1) 함수포인터 형식

void func(int num, const string& str)
{
	cout<< num <<", "<<std<<endl;
}


int main(){
	std::function<void(int, const string&)> f1 = func;
	f1(1, "test");
    return 0;
}

이 예제에서는 auto 키워드를 사용하면 f1앞에 구체적으로 안 써도 되긴 한다. 더 간결하게 표현이 가능하다.

하지만 이렇게 하면 컴파일러는 f1의 타입이 std::function이 아니라 함수 포인터라고 판단한다.

그래서 void(*f1) (int, const string&)라고 반환해버린다.

auto f1 = func;

 

예제 2) 

std::function 타입은 함수 포인터처럼 작동하기 때문에 표준 라이브러리 알고리즘에 인수로 전달 가능하다.

find_if() 알고리즘에 이를 적용한 예는 다음과 같다.

 

bool IsEven(int num)
{
	return num%2 == 0;
}

int main()
{
	vector<int> vec{1,2,3,4,5};
     
    std::function<bool<int>> fcn = IsEven;
    auto result = find_if(cbegin(vec),cend(vec), fcn);
    
    if( result != cend(vec))
    	cout<<"첫 짝수는 : "<<*result<<endl;
    else
    	cout<< "짝수를 찾을 수 없습니다."<<endl;
        
    return 0;
}

 

이 예제들 까지를 보면 굳이 std::function을 사용할 필요가 없다는 생각이 들 수도 있다.

std::function의 진가는 콜백을 클래스의 멤버 변수에 저장해야 할 때와 함수 포인터를 매개변수로 받아야 할 때도 유용하다.

 

 

예제 3) 함수포인터 , 람다 표현식

void process(const vector<int>& vec, function<void(int)> f)
{
	for(auto& i : vec)
    {
    	f(i);
    }
}


void print(int num)
{
	cout<< num << " ";
}

int main()
{
	vector<int> vec{0,1,2,3,4,5};
    
    //print를 함수포인터로 전달해서 호출
    process(vec,print);    
    cout<<endl;
    
    
    //람다 표현식을 std::function 매개변수의 값으로 지정하는 예
    // std::function의 진가!
    // 일반 함수 포인터로는 이렇게 처리할 수 없다.
    int sum = 0;
    process(vec,[&sum](int num) {sum +=num;});
    cout<<"sum = "<< sum <<endl;
    
    return 0;
 }

 

출력 결과

0 1 2 3 4 5
sum = 15

 

 

콜백 매개변수로 std::function을 사용하지 않고, 함수 템플릿으로 만들어도 된다.

template<typename F>
void processTemplate(const vector<int>& vec, F f)
{
	for(auto& i:vec)
	{
    		f(i);
	}
}

이렇게 정의한 함수 템플릿은 비템플릿 process() 함수와 기능이 같다. 

processTemplate()는 일반 함수 포인터와 람다 표현식을 모두 받을 수 있다.

 

가능하면 함수객체보다는 람다 표현식을 사용하기 바란다. 람다 표현식이 코드를 작성하기 쉬울뿐만 아니라 읽거나 이해하기 쉽다.

 

이전에 정리한 람다식 관련

https://hyo-ue4study.tistory.com/324

 

[CPP] 람다식(Lambda Expression)이란?

이름 없는 함수? 예 : 벡터 정렬하기 #include #include using namespace std; bool Compare(int a, int b) { return (a > b); } int main() { vector arr; arr.push back(3); arr.push back(2); arr.push back(7)..

hyo-ue4study.tistory.com

참고서적 :

http://www.yes24.com/Product/Goods/77669043

 

전문가를 위한 C++ - YES24

『전문가를 위한 C++』(개정4판)은 새로 추가된 기능과 관련 도구를 비롯해 C++에 대한 모든 내용을 코드와 솔루션 위주로 소개한다. 저자는 실전 프로그래밍에 적용할 수 있는 현실적인 기법을

www.yes24.com