madf: (Default)
[personal profile] madf
Уявіть собі що у вас є контейнер вказівників на базовий клас і цей контейнер треба відсортувати за типами конкретних об’єктів що лежать у контейнері. Щоб було більше конкретно, живий приклад: маємо журнал виконання певної операції (у моєму випадку то були corporate actions: stock split, merger і spin-off, але це не важливо), треба цю операцію „відкотити“, але порядок виконання операцій при „відкочуванні“ важливий, а записи у журналі можуть іти у довільному порядку.
Проблема у написанні оператора порівняння двох об’єктів за вказівниками (або посиланнями) на базовий клас. Проблема відома під назвою „мультиметоди“ чи „подвійна диспетчеризація“. У C++ зазвичай вирішується або через матрицю відношень, або через dynamic_cast, або через (щасругнусь) паттерн „Visitor“. Але фігачити таблиці порівнянь ліниво, тому використаємо template metaprogramming voodoo щоб заставити компілятор нагенерувати то все самому.
Годі базікати, вйо до коду!

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <memory>
#include <type_traits>

class Base {
public:
virtual void show() const = 0;
virtual bool less(const Base& rhs) const = 0;
};

class A : public Base {
public:
virtual void show() const { std::cout << "A\n"; }
virtual bool less(const Base& rhs) const;
};

class B : public Base {
public:
virtual void show() const { std::cout << "B\n"; }
virtual bool less(const Base& rhs) const;
};

class C : public Base {
public:
virtual void show() const { std::cout << "C\n"; }
virtual bool less(const Base& rhs) const;
};

class D : public Base {
public:
virtual void show() const { std::cout << "D\n"; }
virtual bool less(const Base& rhs) const;
};

template <typename T>
bool isA(const Base& value)
{
return dynamic_cast<const T*>(&value) != nullptr;
}

template <typename T, typename ... Ts>
struct Compare {
template <typename U>
static bool less(const U& a, const Base& b)
{
if (std::is_same<T, U>::value)
return true;
else if (isA<T>(b))
return false;
else
return Compare<Ts ...>::template less<U>(a, b);
}
};

template <typename T>
struct Compare<T> {
template <typename U>
static bool less(const U&, const Base& b)
{
if (std::is_same<T, U>::value)
return true;
else if (isA<T>(b))
return false;
else
return false;
}
};

typedef Compare<A, B, C, D> Order;

bool A::less(const Base& rhs) const { return Order::less(*this, rhs); }
bool B::less(const Base& rhs) const { return Order::less(*this, rhs); }
bool C::less(const Base& rhs) const { return Order::less(*this, rhs); }
bool D::less(const Base& rhs) const { return Order::less(*this, rhs); }

bool operator<(const std::unique_ptr<Base>& lhs, const std::unique_ptr<Base>& rhs) { return lhs->less(*rhs); }

int main()
{
std::vector<std::unique_ptr<Base>> items;
items.push_back(std::unique_ptr<Base>(new C));
items.push_back(std::unique_ptr<Base>(new B));
items.push_back(std::unique_ptr<Base>(new A));
items.push_back(std::unique_ptr<Base>(new D));
std::sort(std::begin(items), std::end(items));
for (const auto& item : items)
item->show();
return 0;
}
_Winnie C++ Colorizer


$ g++ -W -Wall -Wextra -pedantic -std=c++0x main.cpp -o main
$ ./main 
A
B
C
D


typedef задає порядок на множині типів. Визначити метод less для кожного типу все ж таки треба, але тіло метода буде однакове (тут невеликий трюк з виведенням типів). При компіляції з -O3 функцій isA і Compare::less у бінарнику не буде, вони цілком заінлайняться.

Profile

madf: (Default)
madf

April 2018

S M T W T F S
1234567
891011121314
15161718192021
22232425262728
2930     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 6th, 2026 05:20 pm
Powered by Dreamwidth Studios