Функциональные объекты, cv-квалификаторы; контейнеры. Лекция 11. Часть 2

Содержание

Слайд 2

Функциональные объекты

int my_func(int foo, double bar, char* baz, float& bav) {return 0;}
int

Функциональные объекты int my_func(int foo, double bar, char* baz, float& bav) {return
my_func2(int foo, double bar, char* baz, float& bav) {return 2 * foo;}
using MyFuncPtr = int(*)(int, double, char*, float&);
void apply_fn(MyFuncPtr fn) {
  float val = 300.0;
  fn(42, 1337.0, nullptr, val);
}
int main() {
  MyFuncPtr func_ptr;
  func_ptr = my_func;
  float val = -1.0;
  int retval = func_ptr(0, 1, nullptr, val);
  apply_fn(func_ptr);
}

Уже знаем, что функции в С++ являются «гражданами первого сорта» - имена функции являются одновременно идентификаторами со смыслом «указателя на функцию», их можно присваивать и передавать в другие функции.

Слайд 3

Функциональные объекты

class MyFunctor {
public:
  int operator()(int foo, double bar, char* baz, float&

Функциональные объекты class MyFunctor { public: int operator()(int foo, double bar, char*
bav) {
  return 0;
  }
};
int main() {
  MyFunctor functor;
  float val = -1.0;
  int retval = functor(0, 1, nullptr, val);
}

Помимо указателя на функцию и конструктора класса, в выражении типа идентификатор(аргументы) могут также участвовать функциональные объекты (функторы) - объекты класса, который переопределяет operator()

Слайд 4

Функциональные объекты

class MyFunctor {
public:
  int operator()(int foo, double bar, char* baz, float&

Функциональные объекты class MyFunctor { public: int operator()(int foo, double bar, char*
bav) {
  return 0;
  }
};
void apply_functor(MyFunctor fn) {
  float val = 300.0;
  fn(42, 1337.0, nullptr, val);
}
int main() {
  MyFunctor functor;
  apply_functor(functor);
}

Помимо указателя на функцию и конструктора класса, в выражении типа идентификатор(аргументы) могут также участвовать функциональные объекты (функторы) - объекты класса, который переопределяет operator()

Слайд 5

Функциональные объекты

struct MyFunctor {
  int operator()(int foo, double bar, char* baz, float&

Функциональные объекты struct MyFunctor { int operator()(int foo, double bar, char* baz,
bav) {
  return 0;
  }
};
void apply_functor(MyFunctor fn) {
  float val = 300.0;
  fn(42, 1337.0, nullptr, val);
}
int main() {
  MyFunctor functor;
  apply_functor(functor);
}

Часто для функциональных объектов используют struct вместо class - разница лишь в том, что для struct не обязательно указывать public:

Слайд 6

cv-квалификаторы

Наименование cv-qualifiers в стандарте используется для обобщения квалификаторов типов const и volatile:
const

cv-квалификаторы Наименование cv-qualifiers в стандарте используется для обобщения квалификаторов типов const и
- соответствующий идентификатор нельзя модифицировать после создания
volatile - доступы к соответствующему идентификатору запрещено оптимизировать или кэшировать компилятору

int main() {
  int n1 = 0; // non-const object
  const int n2 = 0; // const object
  int const n3 = 0; // const object (same as n2)
  volatile int n4 = 0;  // volatile object
n1 = 1; // ok, modifiable object
//  n2 = 2; // error: non-modifiable object
  n4 = 3; // ok, treated as a side-effect
  const int& r1 = n1; // reference to const bound to non-const object
//  r1 = 2; // error: attempt to modify through reference to const
   const int& r2 = n2; // reference to const bound to const object
}

Слайд 7

cv-квалификаторы

Наименование cv-qualifiers в стандарте используется для обобщения квалификаторов типов const и volatile:
const

cv-квалификаторы Наименование cv-qualifiers в стандарте используется для обобщения квалификаторов типов const и
- соответствующий идентификатор нельзя модифицировать после создания
volatile - доступы к соответствующему идентификатору запрещено оптимизировать или кэшировать компилятору

int main() {
  int n1 = 0;
  const int n2 = 0;
  int const n3 = 0;
  volatile int n4 = 0;
n1 = 1; // ok, modifiable object
//  n2 = 2; // error: non-modifiable object
  n4 = 3; // ok, treated as a side-effect
  const int& r1 = n1; // reference to const bound to non-const object
//  r1 = 2; // error: attempt to modify through reference to const
   const int& r2 = n2; // reference to const bound to const object
}

main:
movl $0, -4(%rsp) # volatile int n4 = 0;
movl $3, -4(%rsp) # n4 = 3;
xorl %eax, %eax # return 0 (implicit)
ret

Скомпилированная программа в ассемблер-представлении

Слайд 8

cv-квалификаторы и функции

using MyFuncPtr = int (*)(int);
int wrong_fn(int& arg) {return 0;}
int ok_fn(const

cv-квалификаторы и функции using MyFuncPtr = int (*)(int); int wrong_fn(int& arg) {return
int arg) {return 0;}
int cv_arg_ok_fn(const volatile int arg) {return 0;}
const int another_wrong_fn(int arg) {return 0;}
const volatile int yet_another_wrong_fn(int arg) {return 0;}
int main()
{
  MyFuncPtr func_ptr;
  // func_ptr = wrong_fn; // error C2440: '=': cannot convert from ...
  func_ptr = ok_fn;
  func_ptr = cv_arg_ok_fn;
  // func_ptr = another_wrong_fn; // error C2440: '=': cannot convert from ...
  // func_ptr = yet_another_wrong_fn; // error C2440: '=': cannot convert from ...
}

cv-qualifiers в аргументах функции не являются частью ее типа...

... также могут быть указаны для возвращаемого значения, но это не имеет какого-то осмысленного эффекта

Слайд 9

cv-квалификаторы и функции

using MyFuncPtr = int (*)(int);
int ok_fn(const int arg) {return 0;}
int

cv-квалификаторы и функции using MyFuncPtr = int (*)(int); int ok_fn(const int arg)
ok_fn(int arg) {return 0;} // error C2084: function 'int ok_fn(const int)’
// already has a body
int ok_fn(volatile int arg) {return 0;} // error C2084: ...
int main()
{
  MyFuncPtr func_ptr;
  func_ptr = ok_fn; // error C2568: '=': unable to resolve function overload
}

Поскольку cv-qualifiers в аргументах функции не являются частью ее типа, нельзя перегружать функцию, поменяв лишь cv-квалификатор ее аргумента(-ов).

Слайд 10

cv-квалификаторы и методы

#include
#include
class Point2D {
public:
  double x, y;
  double get_angle()

cv-квалификаторы и методы #include #include class Point2D { public: double x, y;
{
  return std::atan(y / x);
  }
  void rotate(double angle) {
  double tmp_x = x * std::cos(angle)
+ y * std::sin(angle);
  double tmp_y = -x * std::sin(angle)
+ y * std::cos(angle);
  x = tmp_x; y = tmp_y;
  }
};

Point2D pt_2d = {0.0, 1.0};
std::cout << pt_2d.get_angle() << '\n';
const double M_PI_2 = 1.57079632679489;
pt_2d.rotate(M_PI_2);
std::cout << pt_2d.get_angle() << '\n';

1.5708
6.12323e-17

Слайд 11

cv-квалификаторы и методы

#include
#include
class Point2D {
public:
  double x, y;
  double get_angle()

cv-квалификаторы и методы #include #include class Point2D { public: double x, y;
const {
  return std::atan(y / x);
  }
  void rotate(double angle) const {
  double tmp_x = x * std::cos(angle)
+ y * std::sin(angle);
  double tmp_y = -x * std::sin(angle)
+ y * std::cos(angle);
  x = tmp_x; y = tmp_y;
  }
};

Point2D pt_2d = {0.0, 1.0};
std::cout << pt_2d.get_angle() << '\n';
const double M_PI_2 = 1.57079632679489;
pt_2d.rotate(M_PI_2);
std::cout << pt_2d.get_angle() << '\n';

error C3490: 'x' cannot be modified because it is being accessed through a const object

Слайд 12

cv-квалификаторы и методы

#include
#include
class Point2D {
public:
  double x, y;
  double get_angle()

cv-квалификаторы и методы #include #include class Point2D { public: double x, y;
const {
  return std::atan(y / x);
  }
  void rotate(double angle) {
  double tmp_x = x * std::cos(angle)
+ y * std::sin(angle);
  double tmp_y = -x * std::sin(angle)
+ y * std::cos(angle);
  x = tmp_x; y = tmp_y;
  }
};

const Point2D pt_2d = {0.0, 1.0};
std::cout << pt_2d.get_angle() << '\n';
const double M_PI_2 = 1.57079632679489;
pt_2d.rotate(M_PI_2);

error C2662: 'void Point2D::rotate(double)': cannot convert 'this' pointer from ‘const Point2D' to 'Point2D &'
note: Conversion loses qualifiers

Слайд 13

cv-квалификаторы и методы

#include
#include
class Point2D {
public:
  double x, y;
  double get_angle()

cv-квалификаторы и методы #include #include class Point2D { public: double x, y;
const {
  return std::atan(y / x);
  }
  void rotate(double angle) {
  double tmp_x = x * std::cos(angle)
+ y * std::sin(angle);
  double tmp_y = -x * std::sin(angle)
+ y * std::cos(angle);
  x = tmp_x; y = tmp_y;
  }
};

volatile Point2D pt_2d = {0.0, 1.0};
std::cout << pt_2d.get_angle() << '\n';

error C2662: 'double Point2D::get_angle(void) const': cannot convert 'this' pointer from 'volatile Point2D' to 'const Point2D &'
note: Conversion loses qualifiers

Слайд 14

cv-квалификаторы и методы

#include
#include
class Point2D {
public:
  double x, y;
  double get_angle()

cv-квалификаторы и методы #include #include class Point2D { public: double x, y;
const volatile {
  return std::atan(y / x);
  }
  void rotate(double angle) {
  double tmp_x = x * std::cos(angle)
+ y * std::sin(angle);
  double tmp_y = -x * std::sin(angle)
+ y * std::cos(angle);
  x = tmp_x; y = tmp_y;
  }
};

volatile Point2D pt_2d = {0.0, 1.0};
std::cout << pt_2d.get_angle() << '\n';

OK

1.5708

Слайд 15

cv-квалификаторы и перегрузка методов

#include
#include
class Point2D {
public:
  double x, y;
  double

cv-квалификаторы и перегрузка методов #include #include class Point2D { public: double x,
get_angle() const {
  std::cout << "Const" << '\n';
  return std::atan(y / x);
  }
  double get_angle() {
  std::cout << "Non-const" << '\n';
  return std::atan(y / x);
  }
};

Point2D pt_2d = {0.0, 1.0};
std::cout << pt_2d.get_angle() << '\n';
const Point2D pt_2d_c = {-1.0, -1.0};
std::cout << pt_2d_c.get_angle() << '\n';

Non-const
1.5708
Const
0.785398

Указание cv-qualifiers для методов так, как указано выше, делает соответствующие квалификаторы частью типа метода; методы можно перегружать, меняя лишь cv-qualifier метода, без изменения аргументов и возвращаемых значений. Разрешение перегрузки происходит согласно cv-квалификаторам объекта, метод у которого вызывается.

Слайд 16

const-корректность

#include
#include
class Point2D {
public:
  double x, y;
  double get_angle() {
 

const-корректность #include #include class Point2D { public: double x, y; double get_angle()
return std::atan(y / x);
  }
};

void print_angle(Point2D& pt) {
  std::cout << pt.get_angle();
}
int main()
{
  Point2D pt_2d = {0.0, 1.0};
  print_angle(pt_2d);
  return 0;
}

const-корректность заключается в своевременном и полном указании квалификатора const для аргументов, которым не следует по смыслу меняться в функции, или методов, которые по смыслу не должны менять состояние текущего объекта.

Слайд 17

const-корректность

#include
#include
class Point2D {
public:
  double x, y;
  double get_angle() {
 

const-корректность #include #include class Point2D { public: double x, y; double get_angle()
return std::atan(y / x);
  }
};

void print_angle(Point2D& pt) {
  std::cout << pt.get_angle();
}
int main()
{
  const Point2D pt_2d = {0.0, 1.0};
  print_angle(pt_2d);
  return 0;
}

error C2664: 'void print_angle(Point2D &)': cannot convert argument 1 from 'const Point2D' to 'Point2D &'
note: Conversion loses qualifiers

Слайд 18

const-корректность

#include
#include
class Point2D {
public:
  double x, y;
  double get_angle() {
 

const-корректность #include #include class Point2D { public: double x, y; double get_angle()
return std::atan(y / x);
  }
};

void print_angle(const Point2D& pt) {
  std::cout << pt.get_angle();
}
int main()
{
  const Point2D pt_2d = {0.0, 1.0};
  print_angle(pt_2d);
  return 0;
}

Слайд 19

const-корректность

#include
#include
class Point2D {
public:
  double x, y;
  double get_angle() {
 

const-корректность #include #include class Point2D { public: double x, y; double get_angle()
return std::atan(y / x);
  }
};

void print_angle(const Point2D& pt) {
  std::cout << pt.get_angle();
}
int main()
{
  const Point2D pt_2d = {0.0, 1.0};
  print_angle(pt_2d);
  return 0;
}

error C2662: 'double Point2D::get_angle(void)': cannot convert 'this' pointer from 'const Point2D' to 'Point2D &’
note: Conversion loses qualifiers

Слайд 20

const-корректность

#include
#include
class Point2D {
public:
  double x, y;
  double get_angle() const

const-корректность #include #include class Point2D { public: double x, y; double get_angle()
{
  return std::atan(y / x);
  }
};

void print_angle(const Point2D& pt) {
  std::cout << pt.get_angle();
}
int main()
{
  const Point2D pt_2d = {0.0, 1.0};
  print_angle(pt_2d);
  return 0;
}

OK

Слайд 21

const-корректность

template
class DynArray {
  /* ... */
  const T& operator[](size_t idx) const;
 

const-корректность template class DynArray { /* ... */ const T& operator[](size_t idx)
T& operator[](size_t idx);
  /* ... */
}

для чтения

для записи

Слайд 22

Compile-time error:
error C2280: 'std::hash<_Kty>::hash(const std::hash<_Kty> &)': attempting to reference a deleted function

Compile-time error: error C2280: 'std::hash ::hash(const std::hash &)': attempting to reference a
with
[
_Kty=RGBColor
]

std::unordered_map для пользовательских типов

#include
#include
#include
class RGBColor {
  public: uint8_t r, g, b;
};
using ColorNameMap = std::unordered_map;
using ColorNameMapEntry = std::pair;
int main() {
  ColorNameMap color_map = {
  {{255, 0, 0}, "Red"},
  {{0, 255, 0}, "Green"},
  {{255, 255, 255}, "White"}};
  RGBColor color = {255, 0, 0};
  std::cout << color_map[color];
}

Слайд 23

std::unordered_map

std::unordered_map

Слайд 24

std::unordered_map

template< class Key,     class T,     class Hash = std::hash,     class KeyEqual = std::equal_to,     class Allocator = ...> class unordered_map;

hash(key)

key_equal(key, entry) == true

std::unordered_map template , class KeyEqual = std::equal_to , class Allocator = ...>

Слайд 25

std::unordered_map

template< class Key,     class T,     class Hash = std::hash,     class KeyEqual = std::equal_to,     class Allocator = ...> class unordered_map;

hash(key)

key_equal(key, entry) == true

std::unordered_map template , class KeyEqual = std::equal_to , class Allocator = ...>

Слайд 26

std::unordered_map

template< class Key,     class T,     class Hash = std::hash,     class KeyEqual = std::equal_to,     class Allocator = ...> class unordered_map;

hash(key)

key_equal(key, entry) == true

key_equal(key, entry) == false

std::unordered_map template , class KeyEqual = std::equal_to , class Allocator = ...>

Слайд 27

std::unordered_map для пользовательских типов

std::hash - класс (в виде struct), определяющий operator()(const Key&)

std::unordered_map для пользовательских типов std::hash - класс (в виде struct), определяющий operator()(const
const; оператор должен вычислить хэш пришедшего объекта.

Слайд 28

std::unordered_map для пользовательских типов

std::hash - шаблонный класс, имеющий специализации, правильным образом вычисляющие

std::unordered_map для пользовательских типов std::hash - шаблонный класс, имеющий специализации, правильным образом
хэш для большинства встроенных типов. Для неспециализированного шаблона operator() «удален» специально, поскольку его действие не определено.

Слайд 29

std::unordered_map для пользовательских типов

class RGBColor {
  public: uint8_t r, g, b;
  bool

std::unordered_map для пользовательских типов class RGBColor { public: uint8_t r, g, b;
operator==(const RGBColor& rhs) const {
  return this->r == rhs.r && this->g == rhs.g && this->b == rhs.b;
  }
};
template<>
struct std::hash {
  std::size_t operator()(const RGBColor& c) const
  {
  std::size_t h1 = std::hash{}(c.r);
  std::size_t h2 = std::hash{}(c.g);
  std::size_t h3 = std::hash{}(c.g);
  return h1 ^ (h2 << 1) ^ (h3 << 2);
  }
};

rgbcolor.hpp (вариант 1)

Слайд 30

std::unordered_map для пользовательских типов

#include
#include
#include
#include "rgbcolor.hpp“ // <- contains std::hash

std::unordered_map для пользовательских типов #include #include #include #include "rgbcolor.hpp“ // using ColorNameMap
specialization too
using ColorNameMap = std::unordered_map;
using ColorNameMapEntry = std::pair;
int main() {
  ColorNameMap color_map = {
  {{255, 0, 0}, "Red"},
  {{0, 255, 0}, "Green"},
  {{255, 255, 255}, "White"}};
  RGBColor color = {255, 0, 0};
  std::cout << color_map[color];
}

Red

Слайд 31

std::unordered_map для пользовательских типов

class RGBColor {
  public: uint8_t r, g, b;
  bool

std::unordered_map для пользовательских типов class RGBColor { public: uint8_t r, g, b;
operator==(const RGBColor& rhs) const {
  return this->r == rhs.r && this->g == rhs.g && this->b == rhs.b;
  }
};
struct MyHasher // instead of std::hash specialization
{
  std::size_t operator()(const RGBColor& c) const
  {
  std::size_t h1 = std::hash{}(c.r);
  std::size_t h2 = std::hash{}(c.g);
  std::size_t h3 = std::hash{}(c.g);
  return h1 ^ (h2 << 1) ^ (h3 << 2);
  }
};

rgbcolor.hpp (вариант 2)

Слайд 32

std::unordered_map для пользовательских типов

#include
#include
#include
#include "rgbcolor.hpp“ // <- without std::hash

std::unordered_map для пользовательских типов #include #include #include #include "rgbcolor.hpp“ // using ColorNameMap
specialization, but with MyHasher
using ColorNameMap = std::unordered_map;
using ColorNameMapEntry = std::pair;
int main() {
  ColorNameMap color_map = {
  {{255, 0, 0}, "Red"},
  {{0, 255, 0}, "Green"},
  {{255, 255, 255}, "White"}};
  RGBColor color = {255, 0, 0};
  std::cout << color_map[color];
}

Red

Слайд 33

std::unordered_map

#include
#include
#include
#include "rgbcolor.hpp“ // <- without std::hash specialization, but with

std::unordered_map #include #include #include #include "rgbcolor.hpp“ // using ColorNameMap = std::unordered_map ;
MyHasher
using ColorNameMap = std::unordered_map;
using ColorNameMapEntry = std::pair;
int main() {
  ColorNameMap color_map = {
  {{255, 0, 0}, "Red"},
  {{0, 255, 0}, "Green"},
  {{255, 255, 255}, "White"}};
  RGBColor color = {255, 0, 0};
  std::cout << color_map[color];
}

Red

Слайд 34

std::unordered_map - инвалидация итераторов

std::unordered_map - инвалидация итераторов

Слайд 35

std::unordered_map - инвалидация итераторов

std::unordered_map - инвалидация итераторов

Слайд 36

std::unordered_map - инвалидация итераторов

std::unordered_map - инвалидация итераторов

Слайд 37

std::unordered_map - инвалидация итераторов

std::unordered_map - инвалидация итераторов

Слайд 38

std::unordered_map - множитель нагрузки

std::unordered_map - множитель нагрузки