std::function과 std::bind
Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 104개 중 86번째.
람다는 강력하지만, 때로는 서로 다른 유형의 호출 가능한 객체를 일관된 방식으로 저장하거나 기존 함수를 필요한 시그니처에 맞게 조정해야 합니다. std::function과 std::bind는 <functional> 헤더에서 제공되며 이러한 문제를 해결합니다.
std::function은 특정 시그니처와 일치하는 모든 호출 가능한 대상을 담을 수 있는 타입 소거 래퍼입니다 - function, 람다 또는 함수 객체:
#include <iostream>
#include <functional>
int add(int a, int b) { return a + b; }
int main() {
std::function<int(int, int)> operation;
operation = add; // 일반 함수
std::cout << operation(3, 4) << "\n"; // 7
operation = [](int a, int b) { return a * b; }; // 람다
std::cout << operation(3, 4) << "\n"; // 12
}std::bind는 기존 function의 일부 arguments를 고정하여 새로운 호출 가능 객체를 생성합니다. 계속해서 변경되는 arguments를 표시하려면 std::placeholders::_1, _2 등을 사용하세요:
#include <iostream>
#include <functional>
void greet(const std::string& greeting, const std::string& name) {
std::cout << greeting << ", " << name << "!\n";
}
int main() {
using namespace std::placeholders;
auto sayHello = std::bind(greet, "Hello", _1);
sayHello("Alice"); // Hello, Alice!
auto swapped = std::bind(greet, _2, _1);
swapped("Bob", "Hi"); // Hi, Bob!
}이러한 도구는 OOP에서 콜백을 클래스 멤버로 저장하거나 Strategy 패턴을 구현할 때 특히 유용합니다. 하지만 최신 C++에서는 가독성과 성능을 향상하기 위해 std::bind보다 람다를 선호하는 경우가 많습니다. 인수를 재정렬하거나 레거시 코드와 작업해야 할 때 주로 std::bind를 사용하세요.
챌린지
쉬움std::function과 std::bind의 강력한 기능을 Demonstrate하는 Configure 가능한 calculator를 만들어 보겠습니다. 수학 연산을 런타임에 저장하고, 교체하고, 사용자 지정할 수 있는 시스템을 Create하여 이러한 도구가 유연한 callback 관리 기능을 어떻게 제공하는지 살펴봅니다.
코드를 세 개의 파일로 구성합니다:
MathOperations.h: operation library로 사용할 독립적인 수학 function 모음을 Define합니다.다음 function을 Create합니다:
add(int a, int b): 합을 반환subtract(int a, int b): difference (a - b)를 반환multiply(int a, int b): 곱을 반환power(int base, int exponent, int multiplier):multiplier * (base ^ exponent)를 반환합니다. power를 calculate하기 위해 간단한 loop를 사용합니다 (음이 아닌 exponents라고 assume합니다).
Calculator.h:std::function을 사용하여 operation을 동적으로 저장하고 실행하는Calculatorclass를 Define합니다.Calculator에는 다음이 있어야 합니다:
- current binary operation을 저장하는 private member
std::function<int(int, int)> setOperation(std::function<int(int, int)> op): current operation을 설정calculate(int a, int b): 저장된 operation을 Executes하고 결과를 반환
필요한 header(
<functional>)를 include하고 MathOperations header도 반드시 include하세요.- current binary operation을 저장하는 private member
main.cpp: 세 개의 inputs를 읽습니다:- Operation 이름:
add,subtract,multiply또는square - first number (integer)
- second number (integer)
Calculator를 Create하고 operation 이름에 따라 Configure합니다:add,subtract,multiply의 경우: corresponding function을 calculator에 directly assign합니다.square의 경우:std::bind를 사용하여 exponent 2와 multiplier 1을 always 사용하는power의 specialized version을 Create합니다. bound function은 두 arguments를 받아야 하며, first만 base로 사용합니다 (second argument는 placeholder를 사용하여 무시할 수 있습니다).
operation을 설정한 후, 두 숫자로
calculate()을 Call하고 다음을 출력합니다:Result: [value]그런 다음
a + b + 100을 반환하는 lambda를 calculator에 assign하고, 동일한 inputs로 실행한 뒤 다음을 출력하여 operation을 swapping하는 과정을 Demonstrate합니다:With bonus: [value]- Operation 이름:
예를 들어, inputs가 add, 10, 5인 경우:
Result: 15
With bonus: 115inputs가 multiply, 7, 3인 경우:
Result: 21
With bonus: 110inputs가 square, 4, 0인 경우:
Result: 16
With bonus: 104이 challenge는 std::function이 서로 다른 callable type(일반 function, bound function 및 lambda)을 저장하는 uniform한 방법을 제공하는 반면, std::bind는 일부 arguments를 고정하여 기존 function을 adapt할 수 있게 해 준다는 것을 보여 줍니다.
직접 해보기
#include <iostream>
#include <string>
#include <functional>
#include "Calculator.h"
using namespace std;
int main() {
// 입력 읽기
string operation;
int num1, num2;
cin >> operation >> num1 >> num2;
// Calculator 인스턴스 생성
Calculator calc;
// TODO: 연산 이름에 따라 계산기를 구성하세요
// "add", "subtract", "multiply"의 경우: 해당 함수를 직접 할당하세요
// "square"의 경우: std::bind를 사용하여 power의 특수화된 버전을 만드세요
// 항상 지수 2와 승수 1을 사용하는
// 힌트: 밑 인수에 std::placeholders::_1을 사용하세요
if (operation == "add") {
// 여기에 코드를 작성하세요
} else if (operation == "subtract") {
// 여기에 코드를 작성하세요
} else if (operation == "multiply") {
// 여기에 코드를 작성하세요
} else if (operation == "square") {
// 여기에 코드를 작성하세요 - power 함수와 함께 std::bind를 사용하세요
}
// TODO: calculate()를 호출하고 결과를 출력하세요
// 형식: "Result: [value]"
// TODO: 연산 교체를 시연하세요
// a + b + 100을 반환하는 람다를 계산기에 할당하세요
// calculate()를 다시 호출하고 결과를 출력하세요
// 형식: "With bonus: [value]"
return 0;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
12Modern C++ 주요 기능
이동 시맨틱과 Rvalue완벽한 전달람다 표현식 심화std::function과 std::bindconstexpr와 consteval구조화된 바인딩optional, variant, any직접 연습해 보세요: 온라인 C++ 컴파일러