Notice
Recent Posts
Recent Comments
Link
삶의 공유
C++ 함수 객체(Function Object) 완전 정리 본문
728x90
반응형
📘 C++ 함수 객체(Function Object) 완전 정리
C++ 프로그래밍을 하다 보면 함수를 값처럼 전달해야 하는 경우가 많습니다. 이때 단순 함수 포인터보다 더 강력하고 유연하게 쓸 수 있는 것이 바로 함수 객체(Function Object, Functor) 입니다.
이번 글에서는 함수 객체가 무엇인지, 왜 필요한지, 그리고 STL과 람다와의 관계까지 차근차근 정리해보겠습니다.
1. 함수 객체(Function Object)란?
operator() 연산자를 재정의하여 함수처럼 호출할 수 있는 객체
struct Print {
void operator()(int x) const {
std::println("x = {}", x);
}
};
int main() {
Print p;
p(42); // 함수처럼 호출
// 실제 호출은 p.operator()(42);
}
- p(42) → 사실은 p.operator()(42) 호출
- 즉, 객체이지만 함수처럼 동작하는 특별한 클래스
2. 함수 객체를 사용하는 이유
그렇다면 왜 굳이 함수 대신 함수 객체를 쓸까요? 장점은 다음과 같습니다.
(1) 상태를 가질 수 있다
함수 객체는 클래스이므로 멤버 변수를 가질 수 있습니다.
struct Threshold {
int th;
explicit Threshold(int t): th(t) {}
bool operator()(int x) const { return x >= th; }
};
Threshold check10(10);
std::println("{}", check10(7)); // false
std::println("{}", check10(15)); // true
(2) 클로저(Closure)
람다(lambda)는 사실상 익명 함수 객체입니다.
캡처한 변수들이 함수 객체의 상태가 됩니다.
int base = 5;
auto add_base = [base](int x) { return x + base; };
std::println("{}", add_base(10)); // 15
(3) 인라인 최적화
- 함수 포인터는 인라인 최적화가 어렵지만,
- 함수 객체는 템플릿 인자로 넘기면 인라인될 수 있어 성능에 유리합니다.
std::sort(v.begin(), v.end(), std::greater<>()); // greater는 함수 객체
(4) 이름 충돌 방지
전역 함수 이름 충돌을 피할 수 있습니다.
struct Less { bool operator()(int, int) const; }; 처럼 타입 이름으로 관리되기 때문에 안전합니다.
3. 표준 라이브러리 함수 객체
C++ 표준 <functional> 헤더에는 자주 쓰이는 함수 객체들이 이미 정의되어 있습니다.
- 산술: std::plus<>, std::minus<>, std::multiplies<>
- 비교: std::less<>, std::greater<>
- 논리: std::logical_and<>, std::logical_or<>
- 해시: std::hash<T>
#include <functional>
#include <print>
int main() {
std::plus<int> add;
std::println("{}", add(10, 3)); // 13
}
4. STL 알고리즘과 함수 객체
STL 알고리즘은 함수 객체와 함께 사용할 때 강력해집니다.
(1) 정렬 기준
struct Desc {
bool operator()(int a, int b) const { return a > b; }
};
std::vector<int> v{3,1,4,2};
std::sort(v.begin(), v.end(), Desc{});
// 4,3,2,1
(2) 변환
struct Times {
int k;
explicit Times(int kk): k(kk) {}
int operator()(int x) const { return x * k; }
};
std::vector<int> v{1,2,3}, out(v.size());
std::transform(v.begin(), v.end(), out.begin(), Times{10});
// out = 10, 20, 30
5. 함수 객체와 람다
람다는 사실 익명 함수 객체를 생성하는 문법 설탕입니다.
auto f = [](int x) { return x * 2; };
static_assert(std::is_class_v<decltype(f)>); // 람다도 클래스 타입
즉, 함수 객체의 개념을 이해하면 람다도 자연스럽게 이해할 수 있습니다.
6. std::function 과 함수 객체
- std::function<R(Args...)> 은 모든 호출 가능한 객체(callable) 를 담을 수 있는 래퍼입니다.
- 함수 포인터, 함수 객체, 람다 모두 저장 가능.
- 다만 타입 소거(type erasure)로 인해 성능 오버헤드가 있을 수 있습니다.
#include <functional>
#include <print>
void hello(int n) { std::println("hello {}", n); }
int main() {
std::function<void(int)> f1 = hello; // 함수
std::function<void(int)> f2 = [](int n){ std::println("lambda {}", n); }; // 람다
f1(1);
f2(2);
}
7. 정리
- 함수 객체 = 객체지만 함수처럼 호출 가능
- 함수 포인터보다 강력한 이유:
- 상태 유지 가능
- 람다와 클로저의 기반
- 인라인 최적화 가능
- 이름 충돌 방지
- STL과 표준 함수 객체(std::plus, std::less, std::hash)에서 광범위하게 활용
👉 실무에서는 함수 객체와 람다를 적절히 활용하면, 코드가 더 유연하고 성능도 좋아집니다.
📌 한 줄 요약
함수 객체는 “객체 + 함수”의 장점을 모두 가진 C++의 강력한 도구이며, 현대 C++에서는 람다를 통해 더 직관적으로 사용된다.
반응형
'Programing언어 > C++' 카테고리의 다른 글
| C++에서 Shallow Copy와 Deep Copy 완벽 정리 — 복사의 진짜 차이를 이해하자 (0) | 2025.10.16 |
|---|---|
| C++ ADL (Argument Dependent Lookup) 완벽 정리 (0) | 2025.10.15 |
| C++ 스마트 포인터(Smart Pointer) 완전 정리 (0) | 2025.09.29 |
| [C++] Null Ptr (널포인터) 기본 다지기 (1) | 2024.02.17 |
| [C++] Const, Return, Rvalue - Reference (1) | 2024.02.06 |