Unordered_map - аналог map (то есть контейнер для пар элементов, главным из которых является первый). Внутри unordered_map живёт хэш-таблица, про которые мы с вами ничего не знаем, но когда-нибудь нам расскажут. Важной особенностью хэш-таблицы является работа за О(1), что круто на фоне О(log(n)) в случае map. С другой стороны, для этого мы жертвуем возможностью использовать эту структуру как средство упорядоченья элементов.
Особенности unordered_map: (почти копия из map)
- На второй элемент карты не налагается вообще никаких ограничений и он не участвует ни в каких сравнениях
- В силу данного определения, в unordered_map могут быть положены только те пары, первые элементы которых
- сравнимы на равенство (можно просто перегрузить оператор ==, но обязательно он должен быть const)
- хэшируемы - все базовые и многие встроенные в бибилотеку std типы хэшируются без вашего участия. Для остальных необходима функция хэширования, которую надо реализовать как функтор (см вопрос 12.4) и передать в шаблон unordered_map третьим аргументом. Требования к оператору круглых скобок функтора:
- Принимает объект того же типа, что и key (или его const&)
- Возвращает значение типа size_t
- Метод обязательно должен быть const
- Метод является публичным
- Для каждого объекта метод возвращает как можно более уникальное число (нестрогое требование, которое определяет лишь производительность)
- При желании вы можете дать функтору имя hash<имя_класса_ключа>, засунуть его в пространство имён std, перед классом навесить
template<>
(с пустыми треугольными скобками) - и тогда unordered_map будет подтягивать вашу хэш-функцию без передачи с вашей стороны её в шаблон
- В unordered_map не может лежать две одинаковых по первому элемету пары
- За программиста реализованы создание, уничтожение, копирование и перемещение
- Как и все нормальные контейнеры из библиотеки stl, в unordered_map есть несколько полезных методов, а также прикручены итераторы
- unordered_map - шаблонная, поэтому поддерживает любые классы для второго элемента и любые классы с определённым сравнением для первого элемента
- Существует также unordered_multimap - аналог unordered_map, которая разрешает существование одинаковых по первому элементу пар
Как изобрести динамический полиморфизм в статике с помощью variant и visit
struct A {
void sing(){ cout << "skeak\n"; }
};
struct B {
void sing(){ cout << "phue\n"; }
};
int main() {
vector<variant<A, B>> itemVec = { A{}, B{} };
for (auto item : itemVec) {
visit([](auto a) {a.sing(); return 1; }, item);
}
}
Я крайне не рекомендую создавать указатели на нестатические методы класса - оно всё прекрасно работает, но синтаксис заставит хотеть вас бросить программирование. В примере ниже всё написано таким образом, каким оно работает. Другим образом оно не работает, либо я плохо пытался
//Пример только для нестатических функций. static функции работают нормально и без изврата
//Класс, который имеет метод вывода на экран чего-то там
class A {
public:
int a = 19;
void talk() {
cout << a << endl;
}
};
int main() {
// Указатель создаётся так, будто он - статическое поле в классе
void (A::*bibilov)() = &A::talk; //не потеряйте амперсант, он тут почему-то нужен
A a;
(a.*bibilov)(); //эти избыточные скобки вызывают боль, но они необходимы
//Указатель на метод класса нельзя перепривязать к чему-либо - ни к другому методу, ни к другой функции
//*звуки заставки из Ералаша*
}
Я написал это по приколу и остался доволен результатом
int sum(int a, int b) { return a + b; }
int mult(int a, int b) { return a * b; }
int minus__(int a, int b) { return a - b; }
//Переменное количество параметров для рывка
void do_everything(int a, int b, int amount, int(*ukaz)(int,int)...) {
int (**u)(int, int) = &(ukaz);
while (amount) {
--amount;
cout << (*u)(a, b)<<endl;
++u;
}
}
int main() {
do_everything(3, 4, 3, sum, mult, minus__);
}
// В консоли:
// 7
// 12
// -1
Функция, которая возвращает указатель на функцию, которая возвращает укзаатель на функцию
using func = int(*)(int, int);
//Внезапно для решения этой задачи нам нужна помощь введённого нами типа func
//функции первого порядка
int sum(int a, int b) { return a + b; }
int mult(int a, int b) { return a * b; }
int min(int a, int b) { return (a<b)*a+(b<a)*b; }
int nod(int a, int b) {
if (a == b) return a;
int c = a;
a = min(a, b);
b = mult(c, b) / a;
return nod(a, b - a);
}
int nok (int a, int b){
return mult(a, b) / nod(a, b);
}
//ф, возвращающие указатель на ф
int (*sum_mult(bool a))(int, int) {
if (a) return mult;
return sum;
}
int (*min_sum(bool a))(int, int) {
if (a) return sum;
return min;
}
int (*nok_nod(bool a))(int, int) {
if (a) return nod;
return nok;
}
//третий этаж нашего архитектурного кошмара
//и нет, нельзя func заменить на его определение на ctrl+C -> ctrl+V, оно ругается
func (*getById(int id))(bool) {
switch (id){
case 0: return sum_mult;
case 1: return min_sum;
case 2: return nok_nod;
}
}
int main() {
//выбираем функцию min_sum, в ней выбираем sum, в него передаём числы
cout << getById(1)(true)(3,4) << endl;
}
//В консоли:
//7
Используется весь код тройной вложенности кроме main. Мысленно скопируйте
using func2 = func(*)(bool); //Новый этаж -> новое имя-костыль (определено через старое)
//Ещё немного функций первого порядка - для разнообразия
int sum2(int a, int b) { return (a + b) % 2; }
int mult2(int a, int b) { return (a * b) % 2; }
int sum3(int a, int b) { return (a + b) % 3; }
int mult3(int a, int b) { return (a * b) % 3; }
int sum5(int a, int b) { return (a + b) % 5; }
int mult5(int a, int b) { return (a * b) % 5; }
//Немного функций второго порядка
int (*n2(bool a))(int, int) {
if (a) return mult2;
return sum2;
}
int (*n3(bool a))(int, int) {
if (a) return mult3;
return sum3;
}
int (*n5(bool a))(int, int) {
if (a) return mult5;
return sum5;
}
//Новая функция третьего порядка, теперь их две, можно выбирать
func(*getByVichet(int id))(bool) {
switch (id) {
case 2: return n2;
case 3: return n3;
case 5: return n5;
}
}
//И вот мы на четвёртом этаже
func2(*getFunc(int n))(int) {
switch(n){
case 0: return getById;
case 1: return getByVichet;
}
}
int main() {
cout << getFunc(1)(5)(true)(3,4) << endl;
}
//В консоли:
//2
Стены кода приводить не вижу смысла, потому что std::function почти во всём подобен указателю на функцию. Уточню лишь про вложенности:
//нумерация вложенностей может отличаться от общепринятой
//сигнатуры те же, что и в примерах выше - чтобы удобнее было сравнивать
function<int(int,int)> foo1(bool); //вторая вложенность - возврат функции из функции
function<function<int(int,int)>(bool)> foo2(int); //третья вложенность - функция из функции из функции
function<function<function<int(int,int)>(bool)>(int)> foo3(int); //четвёртый этаж, здрасте