Skip to content

Instantly share code, notes, and snippets.

@NickTikhomirov
Last active January 9, 2020 07:50
Show Gist options
  • Save NickTikhomirov/5b000739d2416bb9ddc00fd0b397ad9a to your computer and use it in GitHub Desktop.
Save NickTikhomirov/5b000739d2416bb9ddc00fd0b397ad9a to your computer and use it in GitHub Desktop.
Дополнительные примеры для моих вопросов

К вопросу 4.1 - особенности std::unordered_map

Unordered_map - аналог map (то есть контейнер для пар элементов, главным из которых является первый). Внутри unordered_map живёт хэш-таблица, про которые мы с вами ничего не знаем, но когда-нибудь нам расскажут. Важной особенностью хэш-таблицы является работа за О(1), что круто на фоне О(log(n)) в случае map. С другой стороны, для этого мы жертвуем возможностью использовать эту структуру как средство упорядоченья элементов.

Особенности unordered_map: (почти копия из map)

  1. На второй элемент карты не налагается вообще никаких ограничений и он не участвует ни в каких сравнениях
  2. В силу данного определения, в unordered_map могут быть положены только те пары, первые элементы которых
    1. сравнимы на равенство (можно просто перегрузить оператор ==, но обязательно он должен быть const)
    2. хэшируемы - все базовые и многие встроенные в бибилотеку std типы хэшируются без вашего участия. Для остальных необходима функция хэширования, которую надо реализовать как функтор (см вопрос 12.4) и передать в шаблон unordered_map третьим аргументом. Требования к оператору круглых скобок функтора:
      • Принимает объект того же типа, что и key (или его const&)
      • Возвращает значение типа size_t
      • Метод обязательно должен быть const
      • Метод является публичным
      • Для каждого объекта метод возвращает как можно более уникальное число (нестрогое требование, которое определяет лишь производительность)
      • При желании вы можете дать функтору имя hash<имя_класса_ключа>, засунуть его в пространство имён std, перед классом навесить template<> (с пустыми треугольными скобками) - и тогда unordered_map будет подтягивать вашу хэш-функцию без передачи с вашей стороны её в шаблон
  3. В unordered_map не может лежать две одинаковых по первому элемету пары
  4. За программиста реализованы создание, уничтожение, копирование и перемещение
  5. Как и все нормальные контейнеры из библиотеки stl, в unordered_map есть несколько полезных методов, а также прикручены итераторы
  6. unordered_map - шаблонная, поэтому поддерживает любые классы для второго элемента и любые классы с определённым сравнением для первого элемента
  7. Существует также unordered_multimap - аналог unordered_map, которая разрешает существование одинаковых по первому элементу пар

К вопросу 11.2 - Variant

Как изобрести динамический полиморфизм в статике с помощью 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);
  }
}

К вопросу 12.1 - Указатели на функции

Как объявлять указатель на метод класса (неприятно)

Я крайне не рекомендую создавать указатели на нестатические методы класса - оно всё прекрасно работает, но синтаксис заставит хотеть вас бросить программирование. В примере ниже всё написано таким образом, каким оно работает. Другим образом оно не работает, либо я плохо пытался

//Пример только для нестатических функций. 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

К вопросу 12.2 - std::function

Стены кода приводить не вижу смысла, потому что 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);   //четвёртый этаж, здрасте
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment