C++ invoke와 apply | "함수 호출" 유틸리티 가이드
이 글의 핵심
C++ invoke와 apply에 대해 정리한 개발 블로그 글입니다. #include <functional> using namespace std;
std::invoke
#include <functional>
using namespace std;
int add(int a, int b) {
return a + b;
}
struct Calculator {
int multiply(int a, int b) {
return a * b;
}
int value = 10;
};
int main() {
// 일반 함수
cout << invoke(add, 2, 3) << endl; // 5
// 람다
auto lambda = { return x * 2; };
cout << invoke(lambda, 5) << endl; // 10
// 멤버 함수
Calculator calc;
cout << invoke(&Calculator::multiply, calc, 3, 4) << endl; // 12
// 멤버 변수
cout << invoke(&Calculator::value, calc) << endl; // 10
}
std::apply
int add(int a, int b, int c) {
return a + b + c;
}
int main() {
tuple<int, int, int> args = {1, 2, 3};
// 튜플 언팩
int result = apply(add, args);
cout << result << endl; // 6
}
실전 예시
예시 1: 제네릭 콜백
template<typename Func, typename... Args>
auto callWithLogging(Func&& func, Args&&... args) {
cout << "함수 호출 시작" << endl;
auto result = invoke(forward<Func>(func), forward<Args>(args)...);
cout << "함수 호출 완료" << endl;
return result;
}
int main() {
auto result = callWithLogging(add, 2, 3);
cout << "결과: " << result << endl;
}
예시 2: 멤버 함수 래퍼
template<typename T, typename Func, typename... Args>
auto callMember(T& obj, Func func, Args&&... args) {
return invoke(func, obj, forward<Args>(args)...);
}
class Widget {
public:
void setName(const string& name) {
this->name = name;
cout << "이름 설정: " << name << endl;
}
string getName() const {
return name;
}
private:
string name;
};
int main() {
Widget w;
callMember(w, &Widget::setName, "MyWidget");
string name = callMember(w, &Widget::getName);
cout << name << endl;
}
예시 3: 튜플 기반 함수 호출
template<typename Func, typename Tuple>
auto callWithTuple(Func&& func, Tuple&& args) {
return apply(forward<Func>(func), forward<Tuple>(args));
}
int multiply(int a, int b, int c) {
return a * b * c;
}
int main() {
auto args = make_tuple(2, 3, 4);
int result = callWithTuple(multiply, args);
cout << result << endl; // 24
}
예시 4: 지연 실행
template<typename Func, typename... Args>
class DeferredCall {
private:
Func func;
tuple<Args...> args;
public:
DeferredCall(Func f, Args... a) : func(f), args(a...) {}
auto execute() {
return apply(func, args);
}
};
template<typename Func, typename... Args>
auto defer(Func func, Args... args) {
return DeferredCall(func, args...);
}
int main() {
auto deferred = defer(add, 2, 3);
cout << "나중에 실행..." << endl;
int result = deferred.execute();
cout << "결과: " << result << endl;
}
invoke_result
template<typename Func, typename... Args>
void printReturnType(Func func, Args... args) {
using ReturnType = invoke_result_t<Func, Args...>;
if constexpr (is_same_v<ReturnType, void>) {
cout << "반환 타입: void" << endl;
} else if constexpr (is_integral_v<ReturnType>) {
cout << "반환 타입: 정수" << endl;
} else {
cout << "반환 타입: 기타" << endl;
}
}
int main() {
printReturnType(add, 1, 2); // 정수
}
멤버 포인터
struct Widget {
int value;
void setValue(int v) {
value = v;
}
int getValue() const {
return value;
}
};
int main() {
Widget w;
// 멤버 함수 포인터
auto setFunc = &Widget::setValue;
invoke(setFunc, w, 42);
// 멤버 변수 포인터
auto valuePtr = &Widget::value;
cout << invoke(valuePtr, w) << endl; // 42
}
자주 발생하는 문제
문제 1: 멤버 함수 호출
// ❌ 직접 호출 불가
auto func = &Widget::getValue;
// func(); // 에러
// ✅ invoke 사용
Widget w;
invoke(func, w);
문제 2: apply 인자 순서
// ❌ 순서 중요
int subtract(int a, int b) {
return a - b;
}
auto args = make_tuple(3, 10);
cout << apply(subtract, args) << endl; // -7 (3 - 10)
// ✅ 순서 확인
auto args2 = make_tuple(10, 3);
cout << apply(subtract, args2) << endl; // 7 (10 - 3)
문제 3: 참조 캡처
// ❌ 복사
int x = 10;
auto args = make_tuple(x);
x = 20;
apply( { cout << y << endl; }, args); // 10
// ✅ 참조
auto args2 = make_tuple(ref(x));
x = 20;
apply( { cout << y << endl; }, args2); // 20
invoke vs 직접 호출
// 직접 호출
add(2, 3);
calc.multiply(3, 4);
// invoke (제네릭)
invoke(add, 2, 3);
invoke(&Calculator::multiply, calc, 3, 4);
invoke 장점:
- 통일된 인터페이스
- 멤버 함수/변수 지원
- 완벽 전달
FAQ
Q1: invoke는 언제 사용하나요?
A:
- 제네릭 콜백
- 멤버 함수 포인터
- 통일된 함수 호출
Q2: apply는 언제 사용하나요?
A:
- 튜플 언팩
- 가변 인자 전달
- 지연 실행
Q3: 성능 오버헤드는?
A: 인라인화로 오버헤드 없음.
Q4: invoke vs 직접 호출?
A: 제네릭 코드에서 invoke 사용.
Q5: apply vs 가변 인자 템플릿?
A:
- apply: 튜플에서 언팩
- 가변 인자: 직접 전달
Q6: invoke/apply 학습 리소스는?
A:
- cppreference.com
- “C++17: The Complete Guide”
- “Effective Modern C++“
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ tuple apply | “튜플 적용” 가이드
- C++ CTAD | “클래스 템플릿 인자 추론” 가이드
- C++ bind | “함수 바인딩” 가이드
관련 글
- C++ tuple apply |
- C++ any |
- C++ bind |
- 모던 C++ (C++11~C++20) 핵심 문법 치트시트 | 현업에서 자주 쓰는 한눈에 보기
- C++ CTAD |