On this page
article
C++ Function object
Function Object (함수 객체)
개념
()
연산자를 재정의하여 호출 가능한 객체 (Functor)- 일반 함수처럼 사용 가능하지만 상태(state)를 가질 수 있음.
장점
- 상태를 가질 수 있음 (예: 내부 변수 유지)
- 인라인 치환성: 특정 상황에서 함수 객체가 함수보다 빠름 (컴파일 시 인라인화 가능)
- 각 함수 객체는 고유 타입을 가짐.
구현 시 유의사항
const
표시: const 인자로도 전달될 수 있도록constexpr
표시: 인자를 컴파일 타임에 파악 가능하다면 컴파일 타임에 계산 가능하게[[nodiscard]]
(C++17): 반환값 무시 시 경고 발생- 템플릿으로 구현하는 경우가 많다
예시 (상태를 가지는 함수 객체)
class F {
int k;
public:
F(int v) : k(v) {}
bool operator()(int n) const { return n % k == 0; } // k의 배수인지 확인
};
// 사용
std::find_if(v.begin(), v.end(), F(3));
Closure (클로저)
- 함수 객체가 스코프 내 변수를 바인딩할 수 있는 일급 함수 객체.
Lambda Expression
으로 생성됨.
Inline 치환성
개념
- 함수 호출 시, 실제 함수 호출을 하지 않고 기계어 코드로 치환하는 것.
상황
- sort 구현 시, 비교 정책을 인자로 받고싶은데 그러면 직접 비교하는 것보다 비용이 증가
- inline으로 받을 수도 없는게 함수 포인터에 주소를 담는 순간 치환되지않으므로
- 그러나 Less로 명시적으로 적어주면 저 자리에 들어갈 수 있는 것이 하나밖에 없기에 인라인 치환 가능
예시
struct Less
{
inline bool operator()(int a, int b) const { return a > b; }
}
void sort(int* x, int sz, bool(*cmp)(int, int))
void sort(int* x, int sz, Less)
{
a < b <-> cmp(a, b)
}
함수 포인터 vs 함수 객체
- 함수 포인터: 주소를 저장 → 인라인 치환 안됨.
- 함수 객체: 템플릿 인자로 전달 → 인라인 치환 가능.
정리
- 함수 인자를 템플릿으로 구성
- 장점: 비교 정책이 인라인 치환 가능, sort함수가 하나하나 생성되니까
- 단점: sort가 많아져서 코드 메모리 증가
- 온라인 컴파일러(godbolt.org)
- 보통 인라인 치환 적용 안해서 보여줌
- x64 msvc v19.lateset, /Ob1 옵션 → 인라인 치환적용하여 확인가능
- 함수 객체 버전은 다 치환돼서 call이 명령어가 없는 걸 확인가능
- 함수 포인터 버전은 call 존재
- cpp이 제공해주는 함수 객체
- functional 헤더
ADL (Argument Dependent Lookup)과 함수 객체
- C++20의 constrained algorithm
- C++98의 알고리즘에 여러 기능 추가 한 것
- std::ranges namespace 안에 제공
- 이유: ADL로 인한 이름 충돌 방지.
ADL
- 개념
- 함수를 찾을 때 인자가 속해 있는 이름 공간은 자동으로 검색한다는 문법
- 필요 이유
- 연산자 재정의 문법에 필요, 편리성
namespace Graphics
{
struct Point
{
int x, y;
};
Point operater+(const Point& p1,
const Point& p2)
{
return point{p1.x + p2.x, p1.y + p2.y};
}
};
int main()
{
Graphics::Point p1{1, 1};
Graphics::Point p2{2, 2};
auto ret2 = p1 + p2; // ADL로 가능
}