type_erasure


Concept: Type Erasure

Type erasure is a powerful technique in C++ that allows you to hide the concrete type of an object behind a common interface. This enables you to work with objects of different types through a unified API, promoting flexibility and reducing coupling in your code. Type erasure is particularly useful when dealing with heterogeneous collections or when you want to decouple interfaces from implementations.

Example 1: Basic Type Erasure with std::function

#include <iostream>
#include <functional>
#include <vector>

class TypeErasedPrintable {
private:
    std::function<void()> m_print_func;

public:
    template<typename T>
    TypeErasedPrintable(const T& obj) : m_print_func([obj]() { std::cout << obj << std::endl; }) {}

    void print() const {
        m_print_func();
    }
};

int main() {
    std::vector<TypeErasedPrintable> objects;
    objects.emplace_back(42);
    objects.emplace_back(3.14);
    objects.emplace_back("Hello, Type Erasure!");

    for (const auto& obj : objects) {
        obj.print();
    }

    return 0;
}

Explanation

This example demonstrates a basic implementation of type erasure using std::function:

Example 2: Custom Type Erasure with Virtual Functions

#include <iostream>
#include <memory>
#include <vector>

class Shape {
public:
    virtual ~Shape() = default;
    virtual void draw() const = 0;
};

template<typename T>
class ShapeModel : public Shape {
private:
    T m_shape;

public:
    ShapeModel(const T& shape) : m_shape(shape) {}

    void draw() const override {
        m_shape.draw();
    }
};

class Circle {
public:
    void draw() const { std::cout << "Drawing a circle" << std::endl; }
};

class Square {
public:
    void draw() const { std::cout << "Drawing a square" << std::endl; }
};

class TypeErasedShape {
private:
    std::unique_ptr<Shape> m_shape;

public:
    template<typename T>
    TypeErasedShape(const T& shape) : m_shape(std::make_unique<ShapeModel<T>>(shape)) {}

    void draw() const {
        m_shape->draw();
    }
};

int main() {
    std::vector<TypeErasedShape> shapes;
    shapes.emplace_back(Circle{});
    shapes.emplace_back(Square{});

    for (const auto& shape : shapes) {
        shape.draw();
    }

    return 0;
}

Explanation

This example shows a more advanced type erasure technique using virtual functions:

Example 3: Type Erasure with External Polymorphism

#include <iostream>
#include <memory>
#include <vector>
#include <string>

class AnimalConcept {
public:
    virtual ~AnimalConcept() = default;
    virtual void make_sound() const = 0;
    virtual std::unique_ptr<AnimalConcept> clone() const = 0;
};

template<typename T>
class AnimalModel : public AnimalConcept {
private:
    T m_animal;

public:
    AnimalModel(const T& animal) : m_animal(animal) {}

    void make_sound() const override {
        m_animal.make_sound();
    }

    std::unique_ptr<AnimalConcept> clone() const override {
        return std::make_unique<AnimalModel>(*this);
    }
};

class Animal {
private:
    std::unique_ptr<AnimalConcept> m_concept;

public:
    template<typename T>
    Animal(const T& animal) : m_concept(std::make_unique<AnimalModel<T>>(animal)) {}

    Animal(const Animal& other) : m_concept(other.m_concept->clone()) {}
    Animal& operator=(const Animal& other) {
        m_concept = other.m_concept->clone();
        return *this;
    }

    void make_sound() const {
        m_concept->make_sound();
    }
};

struct Dog {
    void make_sound() const { std::cout << "Woof!" << std::endl; }
};

struct Cat {
    void make_sound() const { std::cout << "Meow!" << std::endl; }
};

int main() {
    std::vector<Animal> animals;
    animals.emplace_back(Dog{});
    animals.emplace_back(Cat{});

    for (const auto& animal : animals) {
        animal.make_sound();
    }

    return 0;
}

Explanation

This example demonstrates type erasure with external polymorphism:

Summary

Type erasure is a powerful technique in C++ that allows you to work with objects of different types through a common interface. It provides a way to achieve polymorphism without inheritance, enabling more flexible and decoupled designs.

Key points about type erasure:

  1. It allows you to store objects of different types in a single container or use them interchangeably.
  2. It can be implemented using various techniques, including std::function, virtual functions, or external polymorphism.
  3. Type erasure often involves a combination of templates and runtime polymorphism.
  4. It's useful for creating generic interfaces, implementing callbacks, and working with heterogeneous collections.
  5. Type erasure can lead to better encapsulation and more modular code by separating interfaces from implementations.

While type erasure adds some runtime overhead and complexity, it provides significant benefits in terms of flexibility and design. It's a valuable tool in the C++ programmer's toolkit, especially when dealing with complex systems or libraries that need to work with various types without tightly coupling to them.

Related

Previous Page | Course Schedule | Course Content