Type Erasure

TL;DR

  • std::function๊ณผ std::shared_ptr๋Š” ๋ชจ๋‘ Type Erasure ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„๋œ๋‹ค.
  • Type Erasure = ์„œ๋กœ ๊ด€๊ณ„์—†๋Š” ๋‹ค์–‘ํ•œ ํƒ€์ž…์„ ๋‹จ์ผ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ฐ์‹ธ ๊ณตํ†ต์ ์œผ๋กœ ๋‹ค๋ฃจ๋Š” ๊ธฐ๋ฒ•.
  • std::function์€ ํ˜ธ์ถœ ์ธํ„ฐํŽ˜์ด์Šค(call operator)๋ฅผ ์ˆจ๊ธฐ๊ธฐ ์œ„ํ•ด Conceptโ€“Model ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • std::shared_ptr๋Š” deleter(์†Œ๋ฉธ์ž ํ˜ธ์ถœ ๋ฐฉ์‹)๋ฅผ ์ˆจ๊ธฐ๊ธฐ ์œ„ํ•ด ๊ฐ™์€ ํŒจํ„ด์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ๊ณตํ†ต์ :
    • โ€œ์›๋ž˜ ํƒ€์ž… ์ •๋ณดโ€๋ฅผ ๋‚ด๋ถ€์— ์ €์žฅํ•ด ๋Ÿฐํƒ€์ž„์— ์˜ฌ๋ฐ”๋ฅธ ๋™์ž‘(ํ˜ธ์ถœ/์†Œ๋ฉธ)์„ ๋ณด์žฅํ•œ๋‹ค.
    • ๋‚ด๋ถ€์ ์œผ๋กœ virtual dispatch + heap allocation์ด ๋ฐœ์ƒํ•œ๋‹ค.

Overview

1) ์™œ std::function์ด ๊ฐ€๋Šฅํ•œ๊ฐ€?

  • std::function<void(int)>์ด๋ผ๋Š” ๋‹จ์ผ ํƒ€์ž…์—
    • free function
    • lambda
    • functor
      ์™„์ „ํžˆ ์ข…๋ฅ˜๊ฐ€ ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๋ชจ๋‘ ๋Œ€์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • C++์€ ๊ฐ•ํ•œ ์ •์  ํƒ€์ž… ์–ธ์–ด์ธ๋ฐ ์ด๋Ÿฐ ์ผ์ด ๊ฐ€๋Šฅํ•œ ์ด์œ ๋Š” Type Erasure ๋•๋ถ„.

2) Type Erasure ํ•ต์‹ฌ ๊ฐœ๋…

๊ตฌ์„ฑ ์„ค๋ช…
Concept(์ธํ„ฐํŽ˜์ด์Šค) ํ˜ธ์ถœํ•ด์•ผ ํ•˜๋Š” ๊ธฐ๋Šฅ๋งŒ ์ •์˜๋œ ์ˆœ์ˆ˜ ๊ฐ€์ƒ ํด๋ž˜์Šค
Model T ํƒ€์ž…์„ ๊ฐ์‹ธ Concept์„ ๊ตฌํ˜„ํ•œ ๋ž˜ํผ
Container Concept*๋ฅผ ๋ฉค๋ฒ„๋กœ ๊ฐ–๊ณ , ํ˜ธ์ถœ ์‹œ virtual dispatch๋กœ Model์˜ ์‹ค์ œ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰

์ฆ‰,
์—ฌ๋Ÿฌ ํƒ€์ž…(T) โ†’ Model<T>๋กœ ๊ฐ์‹ธ๊ธฐ โ†’ _Concept ํ˜•ํƒœ๋กœ ์ €์žฅ_* โ†’ ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค์ฒ˜๋Ÿผ ์‚ฌ์šฉ
์ด ํ๋ฆ„์ด std::function์˜ ์ •์ฒด๋‹ค.


3) std::shared_ptr๊ฐ€ ๋™์ผ ๊ธฐ๋ฒ•์„ ์“ฐ๋Š” ์ด์œ 

  • std::shared_ptr<void>๋Š” ๊ฐ€๋Šฅํ•œ๋ฐ
  • std::unique_ptr<void>๋Š” ๊ธฐ๋ณธ deleter ๋•Œ๋ฌธ์— ์‚ญ์ œ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

์™œ?

  • unique_ptr<void> โ†’ deleter๊ฐ€ default_delete<void>๋ผ์„œ _void delete๊ฐ€ ๋ถˆ๊ฐ€๋Šฅ_*
  • shared_ptr<void> โ†’ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์›๋ž˜ ๊ฐ์ฒด ํƒ€์ž… T๋ฅผ control block์— ๋ณด๊ด€ โ†’ ์‚ญ์ œํ•  ๋•Œ T์˜ destructor ํ˜ธ์ถœ OK

์ฆ‰, shared_ptr ๋‚ด๋ถ€์—๋„

  • control block + virtual deleter ๊ตฌ์กฐ์˜ Type Erasure ์กด์žฌ.

4) std::function์˜ ๋น„์šฉ

  • virtual ํ˜ธ์ถœ ๋น„์šฉ
  • heap allocation ๋น„์šฉ
    • ๋‹จ, ๊ตฌํ˜„์ฒด์— ๋”ฐ๋ผ SBO(Small Buffer Optimization)๋กœ stack ์ €์žฅ๋  ์ˆ˜๋„ ์žˆ์Œ

Example

1) std::function Type Erasure ๊ตฌ์กฐ (์ถ•์•ฝ ๋ฒ„์ „)

struct Concept {
    virtual void call(int) = 0;
    virtual ~Concept() = default;
};

template<typename T>
struct Model : Concept {
    T obj;
    Model(T o) : obj(o) {}
    void call(int x) override { obj(x); }
};

class Function {
    std::unique_ptr<Concept> ptr;
public:
    template<typename F>
    Function(F f) : ptr(std::make_unique<Model<F>>(f)) {}

    void operator()(int x) { ptr->call(x); }
};

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‹ค์Œ์ด ๋ชจ๋‘ ์ž‘๋™ํ•œ๋‹ค:

Function f1 = printNum;
Function f2 = [](int x){ std::cout << x; };
Function f3 = PrintNumFunctor{};

2) shared_ptr Type Erasure ๊ตฌ์กฐ

struct ControlBlockBase {
    size_t refcount = 1;
    virtual void destroy() = 0;
    virtual ~ControlBlockBase() = default;
};

template<typename T>
struct ControlBlock : ControlBlockBase {
    T* ptr;
    ControlBlock(T* p) : ptr(p) {}
    void destroy() override { delete ptr; }
};

shared_ptr๋Š” ๋‹ค์Œ์„ ์ €์žฅํ•œ๋‹ค:

  • T* obj_ptr
  • ControlBlockBase* ctrl
template<typename T>
class SharedPtr {
    T* ptr;
    ControlBlockBase* ctrl;
public:
    template<typename U>
    SharedPtr(U* p)
        : ptr(p),
          ctrl(new ControlBlock<U>(p)) {}
};

์ฆ‰, U ํƒ€์ž…์„ ๋ณด๊ด€ํ•˜๋Š” ControlBlock<U> ๋•์—
shared_ptr<void>๋กœ๋„ ์›๋ž˜ ํƒ€์ž…์„ ์ •ํ™•ํ•˜๊ฒŒ ์‚ญ์ œ ๊ฐ€๋Šฅํ•˜๋‹ค.

Full Example Code


Takeaways

std::function

  • ์—ฌ๋Ÿฌ ํƒ€์ž…์„ โ€œํ˜ธ์ถœ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒดโ€๋กœ ์ถ”์ƒํ™”ํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ
  • Conceptโ€“Modelโ€“Container ๊ตฌ์กฐ๋ฅผ ์ด์šฉํ•ด ํƒ€์ž… ์ •๋ณด๋ฅผ ์ˆจ๊ธด๋‹ค
  • overhead: virtual call + heap alloc
  • ํ•˜์ง€๋งŒ ์‚ฌ์šฉ์„ฑ์€ ๊ฐ•๋ ฅํ•จ

std::shared_ptr

  • shared_ptr<void>๊ฐ€ ์•ˆ์ „ํ•˜๊ฒŒ ๋™์ž‘ํ•˜๋Š” ์ด์œ ๋Š”
    โ†’ ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก ์‚ญ์ œ ์‹œ ์›๋ž˜ ๊ฐ์ฒด ํƒ€์ž…์˜ deleter๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ
  • ์ด deleter ๋˜ํ•œ virtual ๊ธฐ๋ฐ˜ Type Erasure ๊ตฌ์กฐ
  • unique_ptr<void>์—์„œ delete๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•œ ์ด์œ ๋„ ๊ฐ™์€ ์›๋ฆฌ

  • Type Erasure๋Š” C++ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ณณ๊ณณ์—์„œ ํ•ต์‹ฌ์ ์ธ ์—ญํ• ์„ ํ•œ๋‹ค.
  • โ€œ์ด์งˆ์ ์ธ ํƒ€์ž…๋“ค์„ ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋‹ค๋ฃจ๊ธฐโ€ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ํŒจํ„ด.
  • std::function / std::shared_ptr / std::any / std::packaged_task ๋“ฑ์—์„œ ๋„๋ฆฌ ์‚ฌ์šฉ๋จ.

Reference

Unveiling C++ย Type Erasureย - From std::function to std::any - Sarthak Sehgal - C++Online 2025 CppNorth 2025, Sarthak Sehgal - Unveiling Type Erasure in C++: From std::function to std::any

Type erasure โ€” Part I, Andrzejโ€™s C++ blog Type erasure โ€” Part III, Andrzejโ€™s C++ blog Type erasure โ€” Part III, Andrzejโ€™s C++ blog Type erasure โ€” Part IV, Andrzejโ€™s C++ blog

Back to Basics: Type Erasure - Arthur Oโ€™Dwyer - CppCon 2019