11. std::function 의 타입 소거와 람다 캡처 저장(SBO·값 의미론)
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C++
// 주제: std::function 의 타입 소거(type erasure)와 람다 캡처 저장
// — 소형 버퍼 최적화(SBO)에 따른 힙 할당, 그리고 값(복사) 의미론.
//
// 과제: 아래 프로그램의 출력을 예측하고 다음을 내부 동작으로 설명하라.
// 1) 무캡처 람다 f1, 작은 캡처 f2(int), 큰 캡처 f3(64바이트)를 std::function 에
// 넣을 때 각각 힙 할당이 몇 번 일어나는가? 그 차이는 무엇 때문인가?
// 2) std::function 을 복사(f4 = f3)하면 무슨 일이 일어나는가? 캡처한 상태는
// 공유되는가, 복제되는가? (Tracer 복사 횟수로 관찰)
// 3) std::function 이 "임의의 호출가능 객체를 같은 타입으로 담는" 것을 어떻게
// 가능하게 하는가(타입 소거)? 그 대가(간접 호출·할당)는 무엇인가?
// (할당 횟수의 구체적 수치는 표준 라이브러리 구현에 따라 다를 수 있다.
// libstdc++ 기준으로 설명하라.)
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include <functional>
static int g_allocs = 0;
void* operator new(std::size_t n) { ++g_allocs; return std::malloc(n); }
void operator delete(void* p) noexcept { std::free(p); }
void operator delete(void* p, std::size_t) noexcept { std::free(p); }
struct Tracer {
static int copies;
Tracer() = default;
Tracer(const Tracer&) { ++copies; }
};
int Tracer::copies = 0;
struct Big { char data[64]; };
int main() {
int b;
b = g_allocs;
std::function<int()> f1 = []() { return 1; }; // 무캡처
std::printf("f1 (no capture) allocs=%d\n", g_allocs - b);
b = g_allocs;
int x = 7;
std::function<int()> f2 = [x]() { return x; }; // 작은 캡처(int)
std::printf("f2 (int capture) allocs=%d\n", g_allocs - b);
b = g_allocs;
Big big{};
std::function<int()> f3 = [big]() { return (int)big.data[0]; }; // 큰 캡처(64B)
std::printf("f3 (64B capture) allocs=%d\n", g_allocs - b);
Tracer t;
std::function<int()> g = [t]() { return 0; }; // Tracer 캡처
int cb = Tracer::copies;
std::function<int()> g2 = g; // 함수 객체 복사
std::printf("copy std::function -> Tracer copies=%d\n", Tracer::copies - cb);
return 0;
} 내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.