1. Метапрограммирование

1.1. CRTP (Curiously recurring template pattern)

template<class Derived>
class A
{
public:
    void method()
    {
        static_cast<Derived*>(this)->some_method();
    }
};

class B: A<B>
{
public:
    void some_method()
    {
        lala;
    }
};

1.2. Статическая функция для конструктора

class A: public vector<int>
{
public:
    A(): vector(init())
    {}

private:
    static vector<int> init()
    {
        return ...
    }
};

1.3. Дополнительный класс для инициализации

class A: public vector<double>
{
public:
    // Так не будет работать.
    A(double y): y_(y * y - std::sin(y)), vector({{y_, y_ + 1, y_ + 2}})
    {}

private:
    double y_;
};

class B_helper
{
public:
    B_helper(double y): y_(y * y - std::sqrt(y))
    {}

private:
    double y_;
};

class B: public B_helper, public vector<double>
{
public:
    B(double y): B_helper(y), vector({{y_, y_ + 1, y_ + 2}})
    {}
};

1.4. Расширение функциональности через наследование

struct A
{
    void some_method();
    void another_method();
}

struct B: A
{
    void extra_method();
}

1.5. Атовыводимость типов для конструктора

template<class V>
class A
{
public:
    template<class B>
    A(B x)
    { ... }
};

template<class B, V = deduce B>
A<V> make(B x)
{
    return A<V>(x);
}

1.6. Константность методов

template<class T>
class iterator
{
public:
    T& operator*() const
    {
        return *ptr_;
    }

private:
    T* ptr_;
};

1.7. Замена виртуальности

class Lala
{
public:
    void method();
};

class Lala2: public Lala
{
public:
    void method()
    {
        // override
    }
};

class Lala3
{
public:
    void method()
    {
        // new implementation
    }
};

std::variant<Lala, Lala2, Lala3> obj = ...;
std::visit([](auto& x) { x.method(); }, obj)

2. SOLID

2.1. (S) Принцип единственной ответственности

Пример

Конвертер форматов:

Решение

Подход pandoc:

2.2. (O) Принцип открытости/закрытости

2.3. (L) Принцип подстановки Барбары Лисков

Список с дублированными элементами.

test<ListType>():
    ListType l;
    l.add(44)
    CHECK(l.size() == 1)

DoubleList(List):
    add(x):
        List::add(x)
        List::add(x)

test<DoubleList>() // fail

Как быть?

2.4. (I) Принцип разделения интерфейса

Объединённый интерфейс Разделённый интерфейс
driver:
    allocate(size): void*
    deallocate(void*)
    set_program(byte[])
    read(ptr, count, offset): vector<byte>
    write(ptr, count, offset)
    enqueue()
    wait()
allocator:
    allocate(size): void*
    deallocate(void*)

driver_data:
    ptr
    count
    offset

io_handler:
    enqueue(driver_data)
    wait()

driver(allocator):
    set_program(byte[]) -> io_handler

2.5. (D) Принцип инверсии зависимостей

Встроенная зависимость Зависимость вне класса
tq:
    - tile:
        ptr
        count

    - vector<tile>

    push(ptr, count)
    begin(): vector<tile>::iterator
    end(): vector<tile>::iterator
tq2<Tile>:
    - vector<Tile>
    push<Ptr>(ptr, count)
    begin(): vector<Tile>::iterator
    end(): vector<Tile>::iterator