Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Лекция №2. Вывод типов.

Baramiya Denis
September 13, 2018

Лекция №2. Вывод типов.

1. Rvalue-ссылки.
2. Правила вывода типа шаблона.
3. Универсальные ссылки.
4. Вывод типа auto.
5. Отличие вывода типа auto от вывода типа шаблона.
6. std::initializer_list
7. Унифицированная инициализация.
8. Преимущества и недостатки фигурной инициализации.

Baramiya Denis

September 13, 2018
Tweet

More Decks by Baramiya Denis

Other Decks in Education

Transcript

  1. RVALUE-ССЫЛКИ • lvalue-выражение – соответствует объектам, на которые можно ссылаться

    по имени, следуя указателю или lvalue-ссылке. У lvalue-выражения всегда можно получить адрес. • rvalue-выражение – соответствует временным объектам. У rvalue-выражения нельзя получить адрес. • Type & – lvalue-ссылка. Можно связать только с lvalue объектом. • Type && – rvalue-ссылка. Концептуально, считается ссылкой на временный объект. Но может быть связана как с rvalue объектом, так и с lvalue объектом (путем приведения типа).
  2. int obj = 0; int& obj_lref = obj; // lvalue-ссылка

    int& obj_lref2 = 1; // Error! int&& obj_rref = 1; // rvalue-ссылка int&& obj_rref2 = obj; // Error! int&& obj_rref3 = static_cast<int&&>(obj); // rvalue-ссылка int&& obj_rref4 = std::move(obj); // rvalue-ссылка struct Point { public: ... Point(Point&& rhs); ... }; rhs является lvalue объектом, хотя и имеет ссылочный тип rvalue
  3. ВЫВОД ТИПА ШАБЛОНА // Псевдокод шаблона функции template <typename T>

    void func(ParamType param); func(expr); // Вывод T и ParamType из expr. template <typename T> void func(const T& param); // ParamType - const T& int obj = 0; func(obj); // Вывод func с параметром int. // T - int. // ParamType - const int&.
  4. ТРИ СЦЕНАРИЯ ВЫВОДА • ParamType является указателем или ссылкой, но

    не универсальной ссылкой. • ParamType является универсальной ссылкой. • ParamType не является ни указателем, ни ссылкой.
  5. 1. ParamType является указателем или ссылкой, но не универсальной ссылкой

    Правила вывода типа T: 1. Если типом expr является ссылка, ссылочная часть игнорируется. 2. Затем выполняется сопоставление типа expr c ParamType для определения T. template <typename T> void func(ParamType param); func(expr);
  6. 1. ParamType является указателем или ссылкой, но не универсальной ссылкой

    template <typename T> void func(T& param); int x = 27; // x имеет тип int const int cx = x; // cx имеет тип const int const int& rx = x; // rx имеет тип const int& func(x); // T - int, тип param - int& func(cx); // T - ???, тип param - ??? func(rx); // T - ???, тип param - ???
  7. 1. ParamType является указателем или ссылкой, но не универсальной ссылкой

    template <typename T> void func(T& param); int x = 27; // x имеет тип int const int cx = x; // cx имеет тип const int const int& rx = x; // rx имеет тип const int& func(x); // T - int, тип param - int& func(cx); // T - const int, тип param - const int& func(rx); // T - const int, тип param - const int&
  8. 1. ParamType является указателем или ссылкой, но не универсальной ссылкой

    template <typename T> void func(const T& param); // Добавим модификатор const int x = 27; // x имеет тип int const int cx = x; // cx имеет тип const int const int& rx = x; // rx имеет тип const int& func(x); // T - ???, тип param - ??? func(cx); // T - ???, тип param - ??? func(rx); // T - ???, тип param - ???
  9. 1. ParamType является указателем или ссылкой, но не универсальной ссылкой

    template <typename T> void func(const T& param); // Добавим модификатор const int x = 27; // x имеет тип int const int cx = x; // cx имеет тип const int const int& rx = x; // rx имеет тип const int& func(x); // T - int, тип param - const int& func(cx); // T - int, тип param - const int& func(rx); // T - int, тип param - const int&
  10. 1. ParamType является указателем или ссылкой, но не универсальной ссылкой

    template <typename T> void func(T* param); // Теперь param является указателем int x = 27; // x имеет тип int const int* px = &x; // px имеет тип const int* func(&x); // T - ???, тип param - ??? func(px); // T - ???, тип param - ???
  11. 1. ParamType является указателем или ссылкой, но не универсальной ссылкой

    template <typename T> void func(T* param); // Теперь param является указателем int x = 27; // x имеет тип int const int* px = &x; // px имеет тип const int* func(&x); // T - int, тип param - int* func(px); // T - const int, тип param - const int*
  12. 2. ParamType является универсальной ссылкой Правила вывода типа T: •

    Если expr представляет собой lvalue, то T и ParamType выводятся как lvalue-ссылки. • Если expr представляет собой rvalue, то применяются "обычные" правила (из случая 1). template <typename T> void func(ParamType param); func(expr); Универсальная ссылка: ParamType объявлен как T&&
  13. 2. ParamType является универсальной ссылкой template <typename T> void func(T&&

    param); int x = 27; // x имеет тип int const int cx = x; // cx имеет тип const int const int& rx = x; // rx имеет тип const int& func(x); // x - lvalue, T - int&, тип param - int& func(cx); // cx - ???, T - ???, тип param - ??? func(rx); // rx - ???, T - ???, тип param - ??? func(27); // 27 - ???, T - ???, тип param - ??? func(std::move(x)); // std::move(x) - ???, T - ??? // тип param - ???
  14. 2. ParamType является универсальной ссылкой template <typename T> void func(T&&

    param); int x = 27; // x имеет тип int const int cx = x; // cx имеет тип const int const int& rx = x; // rx имеет тип const int& func(x); // x - lvalue, T - int&, тип param - int& func(cx); // cx - lvalue, T - const int&, тип param - const int& func(rx); // rx - lvalue, T - const int&, тип param - const int& func(27); // 27 - rvalue, T - int, тип param - int&& func(std::move(x)); // std::move(x) - rvalue, T - int // тип param - int&&
  15. 3. ParamType не является ни указателем, ни ссылкой Правила вывода

    типа T: 1. Если типом expr является ссылка, ссылочная часть игнорируется. 2. Если после отбрасывания ссылочной части expr является const, это также игнорируется. template <typename T> void func(ParamType param); func(expr); Передача по значению: ParamType объявлен как T
  16. template <typename T> void func(T param); int x = 27;

    // x имеет тип int const int cx = x; // cx имеет тип const int const int& rx = x; // rx имеет тип const int& func(x); // T - ???, тип param - ??? func(cx); // T - ???, тип param - ??? func(rx); // T - ???, тип param - ??? 3. ParamType не является ни указателем, ни ссылкой
  17. template <typename T> void func(T param); int x = 27;

    // x имеет тип int const int cx = x; // cx имеет тип const int const int& rx = x; // rx имеет тип const int& func(x); // T - int, тип param - int func(cx); // T - int, тип param - int func(rx); // T - int, тип param - int 3. ParamType не является ни указателем, ни ссылкой
  18. template <typename T> void func(T param); const char* const ptr

    = // ptr - константный указатель на "Fun with pointers"; // константный объект func(ptr); // тип param - ??? 3. ParamType не является ни указателем, ни ссылкой
  19. template <typename T> void func(T param); const char* const ptr

    = // ptr - константный указатель на "Fun with pointers"; // константный объект func(ptr); // тип param - const char* 3. ParamType не является ни указателем, ни ссылкой
  20. template <typename T> void func1(T param); template <typename T> void

    func2(T& param); const char ptr[] = "Hello world"; // ptr - const char[12] func1(ptr); // T - const char*, param - const char* func2(ptr); // T - const char[12], param - const char (&)[12] Аргументы-массивы
  21. template <typename T> void func1(T param); template <typename T> void

    func2(T& param); void someFunc(int, double); // тип - void(int, double) func1(someFunc); // param - void(*)(int, double) func2(someFunc); // param - void(&)(int, double) Аргументы-функции
  22. ВЫВОД ТИПА AUTO auto x = 27; const auto cx

    = x; const auto& rx = x; template <typename T> void func_for_x(T param); func_for_x(27); template <typename T> void func_for_cx(const T param); func_for_cx(x); template <typename T> void func_for_rx(const T& param); func_for_rx(x); Работают правила вывода типа шаблона
  23. ВЫВОД ТИПА AUTO auto x = 27; // x имеет

    тип int const auto cx = x; // cx имеет тип const int const auto& rx = x; // rx имеет тип const int& auto&& uref1 = x; // x - int и lvalue, uref1 - int& auto&& uref2 = cx; // cx - const int и lvalue, uref2 - const int& auto&& uref3 = 27; // 27 - int и rvalue, uref3 - int&&
  24. int x1 = 27; // C++98 int x2(27); // C++98

    int x3 = {27}; // C++11 int x4 {27}; // C++11 // Заменим на ключевое слово auto auto x1 = 27; // тип int auto x2(27); // тип int auto x3 = {27}; // тип std::inializer_list<int> auto x4 {27}; // тип std::inializer_list<int> до С++17 // тип int начиная с C++17 auto x5 {27, 1}; // тип std::inializer_list<int> до С++17 // Error! начиная с C++17 Рассмотрение инициализаторов в фигурных скобках является единственным отличием вывода типа auto от вывода типа шаблона.
  25. auto x1 = { 1, 2 }; // тип x

    - std::inializer_list<int> auto x2 = { 1, 2. }; // Error! Ошибка вывода типа T для // шаблона std::inializer_list<T> template <typename T> void func1(T param); func1({1, 2, 3}); // Error! Ошибка вывода типа T // для шаблона func1<T>. template <typename T> void func2(std::initializer_list<T> param); func2({1, 2, 3}); // T - int, // тип param - std::initializer_list<int>
  26. ПРЕИМУЩЕСТВА {} • Нет «обрезания» типов. char можно присвоить int,

    но не наоборот. Аналогично с double и float. • Нет преобразования между целочисленными типами и типами с плавающей точкой.
  27. void f(double val, int val2) { int x2 = val;

    // если val==7.9, x2 <- 7 char c2 = val2; // если val2==1025, c2 <- 1 int x3 {val}; // ОШИБКА char c3 {val2}; // ОШИБКА char c4 {24}; // OK. 24 входит в char char c5 {264}; // ОШИБКА. 264 не входит в 8-битный char int x4 {2.0}; // ОШИБКА. int и double. // ... }
  28. int x4 {}; // 0 double d4 {}; // 0.0

    char *p {}; // nullptr vector<int> v4{}; // пустой вектор string s4 {}; // пустая строка char buf[1024] {}; // все символы 0 vector<int> vec(); // объявили функцию {} — значение по умолчанию
  29. struct Work { string author; string name; int year; };

    // Инициализация почленно Work s9 { "Beethoven", "Symphony No. 9 in D minor, Op. 125; Choral", 1824 }; // Конструктор копирования по умолчанию Work currently_playing { s9 }; Work none {}; // аналог: { {}, {}, {} } или { "", "", 0 } Инициализация
 объектов классов без конструкторов
  30. struct X { X(int); }; X x0; // ОШИБКА. Нет

    инициализации X x1 {}; // ОШИБКА. Пустая инициализация X x2 {2}; // ОК X x3 {"two"}; // ОШИБКА. Не тот тип. X x4 {1,2}; // ОШИБКА. Неверное количество X x5 {x4}; // OK. Конструктор копирования по умолч. Инициализация с конструктором
  31. struct S1 { int a, b; }; struct S2 {

    int a, b; S2(int aa = 0, int bb = 0) : a(aa), b(bb) {} }; S1 x11(1,2); // ОШИБКА. Нет конструктора S1 x12 {1,2}; // OK. Почленная инициализация S1 x13(1); // ОШИБКА. Нет конструктора S1 x14 {1}; // OK. x14.b <- 0 S2 x21(1,2); // ОК. Конструктор S2 x22 {1,2}; // ОК. Конструктор S2 x23(1); // OK. Конструктор с арг. по умолч. S2 x24 {1}; // OK. Конструктор с арг. по умолч.
  32. vector<double> v { 1, 2, 3.456, 99.99 }; list<pair<string,string>> languages

    { { "Nygaard", "Simula" }, { "Richards", "BCPL" }, { "Ritchie", "C"} }; map<vector<string>,vector<int>> years { { { "Maurice", "Vincent", "Wilkes" }, { 1913, 1945, 1951, 1967, 2000 } }, { { "Martin", "Richards"}, { 1982, 2003, 2007 } }, { { "David", "John", "Wheeler"}, { 1927, 1947, 1951, 2004 } } }; // НО КАК?!! Произвольное количество
 аргументов конструктора
  33. #include <initializer_list> #include <iostream> void f(std::initializer_list<int> list) { std::cout <<

    "Size = " << list.size() << std::endl; for (int x: list) std::cout << x << std::endl; } f({1, 2}); f({23, 345, 4567, 56789}); f({}); // Пустой список std::initializer_list<T>
  34. • Если можно вызвать конструктор по умолчанию или конструктор со

    списком, используется первый. • Если можно вызвать конструктор со списком или «обычный» конструктор, также используется первый. struct X { X(initializer_list<int>); X(); X(int); }; X x0 {}; // X() X x1 ({}); // X(initializer_list<int>) X x2 {1}; // X(initializer_list<int>)
  35. vector<int> v1 {1}; // Вектор из одного элемента: 1 vector<int>

    v2 {1,2}; // Вектор из двух элементов: 1,2 vector<int> v3(1); // Вектор из одного элемента: 0 (по умолч.) vector<int> v4(1,2); // Один элемент со значением 2 Разница между () и {}