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

Лекция №4. Современный С++.

Baramiya Denis
September 26, 2018

Лекция №4. Современный С++.

1. Объявление псевдонимов (alias declaration).
2. Применение decltype и его особенности.
3. std::move.
4. Универсальные ссылки (universal references).
5. Свертывание ссылок (reference collapsing).
6. std::forward.
7. Прямая передача (perfect forwarding).

Baramiya Denis

September 26, 2018
Tweet

More Decks by Baramiya Denis

Other Decks in Education

Transcript

  1. ОБЪЯВЛЕНИЕ ПСЕВДОНИМА (ALIAS DECLARATION) std::unique_ptr<std::unordered_map<std::string, std::string>> ptr; // typedef specifier

    typedef std::unique_ptr<std::unordered_map<std::string, std::string>> UPtrMapSS; // alias declaration using UPtrMapSS = std::unique_ptr<std::unordered_map<std::string, std::string>>;
  2. ПРЕИМУЩЕСТВА ПСЕВДОНИМОВ • Более простое для восприятия при работе с

    типами, включающие указатели на функции. • Объявления псевдонимов могут быть шаблонизированы.
  3. // FP является синонимом для указателя на функцию, // принимающую

    int и const std::string& и ничего не // возвращающую typedef void (*FP) (int, const std::string&); // То же самое, но как объявление псевдонима using FP = void (*) (int, const std::string&);
  4. ШАБЛОНЫ ПСЕВДОНИМОВ // MyAlloc - пользовательский распределитель // памяти. template

    <typename T> struct MyAllocList { typedef std::list<T, MyAlloc<T>> type; }; MyAllocList<ObjectType>::type lw; // Клиентский // код // Все хуже, когда MyAllocList используется // как член-данные template <typename T> struct Widget { private: //MyAllocList<T>::type - dependent type typename MyAllocList<T>::type list; }; template <typename T> using MyAllocList = std::list<T, MyAlloc<T>>; //Убирается суффикс "::type" MyAllocList<ObjectType> lw; // Клиентский // код template <typename T> struct Widget { private: //Ни typename, ни ::type MyAllocList<T> list; };
  5. #include <type_traits> std::remove_const<T>::type //C++11 : const Т -> Т std::remove_reference<T>::type

    //C++11 : Т& / Т&& -> Т std::add_lvalue_reference<T>::type //C++11 : Т -> Т& template <typename T> using remove_const_t = typename std::remove_const<T>::type; template <typename T> using remove_reference_t = typename std::remove_reference<T>::type; template <typename T> using add_lvalue_reference_t = typename std::add_lvalue_reference<T>::type; std::remove_const_t<T> //C++14 : const Т -> Т std::remove_reference_t<T> //C++14 : Т& / Т&& -> Т std::add_lvalue_reference_t<T> //C++14 : Т -> Т&
  6. DECLTYPE const int i = 0; // decltype(i) - const

    int bool f (const Widget &w); // decltype(w) - const Widget& // decltype(f) - bool (const Widget&) struct Point{ int x, y; // decltype(Point::x) - int }; // decltype(Point::y) - int template <typename T> class vector{ public: ... T& operator[](size_t index); } vector<int> v; // decltype(v) - vector<int> if(v[0] == 0) ... // decltype(v[0]) - int&
  7. // C++11 - завершающий возвращаемый тип template <typename Container, typename

    Index> auto authAndAccess(Container &c, Index i) -> decltype(c[i]) { authenticateUser(); return c[i]; //Возвращаемый тип - ??? } // C++14 - вывод типа шаблона template <typename Container, typename Index> auto authAndAccess(Container &c, Index i) { authenticateUser(); return c[i]; //Возвращаемый тип - ??? } ПРИМЕНЕНИЕ DELCTYPE
  8. // C++11 - завершающий возвращаемый тип template <typename Container, typename

    Index> auto authAndAccess(Container &c, Index i) -> decltype(c[i]) { authenticateUser(); return c[i]; //Возвращаемый тип - T& } // C++14 - вывод типа шаблона template <typename Container, typename Index> auto authAndAccess(Container &c, Index i) { authenticateUser(); return c[i]; //Возвращаемый тип - T } ПРИМЕНЕНИЕ DELCTYPE
  9. // C++14 - вывод типа шаблона template <typename Container, typename

    Index> auto authAndAccess(Container &c, Index i) { authenticateUser(); return c[i]; //Возвращаемый тип - T } std::deque<int> d; ... authAndAccess(d, 5) = 10; //Ошибка компиляции! // Решение // C++14 - вывод типа используя правила decltype template <typename Container, typename Index> decltype(auto) authAndAccess(Container &c, Index i) { authenticateUser(); return c[i]; //Возвращаемый тип - T& } ПРИМЕНЕНИЕ DELCTYPE
  10. int a = 0; const int& cref = a; auto

    val1 = cref; //Вывод типа auto - int decltype(auto) val2 = cref; //Вывод типа decltype - const int& ОБЪЯВЛЕНИЕ ПЕРЕМЕННЫХ
  11. ОСОБЕННОСТИ DECLTYPE • Для lvаluе-выражений типа T, отличных от имени,

    decltype всегда дает тип Т&. decltype(auto) f1(){
 int x = 0; ... return x; //decltype(x) -> int } decltype(auto) f2(){
 int x = 0; ... return (x); //decltype((x)) -> int& }
  12. STD::MOVE //C++11 template<typename T> typename remove_reference<T>::type&& move(T&& param) { using

    ReturnType = typename remove_reference<T>::type&&; return static_cast<ReturnType>(param); } //C++14 template<typename T> decltype(auto) move(T&& param) { using ReturnType = remove_reference_t<T>&&; return static_cast<ReturnType>(param); }
  13. STD::FORWARD void process(const Widget& lvalArg); void process(Widget&& rvalArg); template<typename T>

    void logAndProcess(T&& param) { auto now = std::chrono::system_clock::now(); makeLogEntry("Вызов process", now); process(std::forward<T>(param)); } Widget w; ... logAndProcess(w); //Вызов с lvalue logAndProcess(std::move(w)); //Вызов с rvalue «Условное приведение»
  14. • std::move выполняет безусловное приведение к rvalue. Сама по себе

    эта функция не перемещает ничего. • std::forward приводит свой аргумент к rvalue только тогда, когда этот аргумент связан с rvalue. • Ни std::move, ни std::forward не выполняют никаких действий времени выполнения. ИТАК, STD::MOVE И STD::FORWARD
  15. ОТЛИЧИЕ УНИВЕРСАЛЬНЫХ ССЫЛОК ОТ RVALUE-ССЫЛОК void f(Widget &&param); // rvalue-ссылка

    Widget&& var1 = Widget(); // rvalue-ссылка auto&& var2 = var1; // универсальная ссылка template <typename T> void f(std::vector<T>&& param); // rvalue-ссылка template <typename T> void f(T&& param); // универсальная ссылка template <typename T> void f(const T&& param); // rvalue-ссылка
  16. УСЛОВИЯ ДЛЯ УНИВЕРСАЛЬНЫХ ССЫЛОК • Наличие вывода типа. • Вид

    объявления ссылки должен быть "T&&". template <class T, class Allocator = allocator<T>> class vector { public: // ... void push_back(T&& x); // rvalue-ссылка // ... };
  17. • Если параметр шаблона функции имеет тип T&& для выводимого

    типа Т или если объект объявлен с использованием auto&&, то параметр или объект является универсальной ссылкой. • Если вид объявления типа не является в точности type&& или если вывод типа не имеет места, то tуре&& означает rvalue- ccылкy. • Универсальные ссылки соответствуют rvаluе-ссылкам, если они инициализируются значениями rvalue. Они соответствуют lvаluе- ссылкам, если они инициализируются значениями lvalue. СЛЕДУЕТ ЗАПОМНИТЬ
  18. Widget makeWidget(){ Widget w; ... return w; } Widget makeWidget(){

    Widget w; ... return std::move(w); } Никогда не применяйте std::move и std::forward к локальным объектам, которые могут быть объектом оптимизации возвращаемого значения. Так делать плохо! RVO не работает.
  19. СВЕРТЫВАНИЕ ССЫЛОК (REFERENCE COLLAPSING) template <typename T> void f(T&& param);

    // универсальная ссылка Widget widgetFactory(); // Функция, возвращающая rvalue Widget w; // Переменная lvalue f(w); // Вызов функции с lvalue; // тип T - ??? f(widgetFactory()); // Вызов функции с rvalue; // тип T - ???
  20. СВЕРТЫВАНИЕ ССЫЛОК (REFERENCE COLLAPSING) template <typename T> void f(T&& param);

    // универсальная ссылка Widget widgetFactory(); // Функция, возвращающая rvalue Widget w; // Переменная lvalue f(w); // Вызов функции с lvalue; // тип T - Widget& f(widgetFactory()); // Вызов функции с rvalue; // тип T - Widget
  21. СВЕРТЫВАНИЕ ССЫЛОК (REFERENCE COLLAPSING) int x; ... auto& & rx

    = x; //Ошибка! Объявлять ссылки на ссылки нельзя. template <typename T> void f(T&& param); Widget w; f(w); // T -> Widget& void f(Widget& && param); void f(Widget& param); How???
  22. STD::FORWARD template <typename T> void f(T&& param){ ... someFunc(std::forward<T>(param)); }

    template <typename T> T&& forward(remove_reference_t<T>& param){ return static_cast<T&&>(param); } Widget w; // Переменная lvalue f(w); // Вызов функции с lvalue; // тип T - Widget& f(widgetFactory()); // Вызов функции с rvalue; // тип T - Widget
  23. ВЫЗОВ STD::FORWARD С LVALUE template <typename T> Widget& && forward(remove_reference_t<Widget&>&

    param){ return static_cast<Widget& &&>(param); } template <typename T> Widget& && forward(Widget& param){ return static_cast<Widget& &&>(param); } template <typename T> Widget& forward(Widget& param){ return static_cast<Widget&>(param); }
  24. ВЫЗОВ STD::FORWARD С RVALUE template <typename T> Widget&& forward(remove_reference_t<Widget>& param){

    return static_cast<Widget&&>(param); } template <typename T> Widget&& forward(Widget& param){ return static_cast<Widget&&>(param); }
  25. 1. ИНСТАНЦИРОВАНИЕ ШАБЛОНА template <typename T> void f(T&& param); //

    универсальная ссылка Widget widgetFactory(); // Функция, возвращающая rvalue Widget w; // Переменная lvalue f(w); // Вызов функции с lvalue; // тип T - Widget& f(widgetFactory()); // Вызов функции с rvalue; // тип T - Widget
  26. 2. ГЕНЕРАЦИЯ ТИПОВ ДЛЯ ПЕРЕМЕННЫХ AUTO Widget widgetFactory(); // Функция,

    возвращающая rvalue Widget w; // Переменная lvalue auto&& w1 = w; auto&& w2 = widgetFactory(); Widget& && w1 = w; Widget& w1 = w; Widget&& w2 = widgetFactory();
  27. 3. ГЕНЕРАЦИЯ И ИСПОЛЬЗОВАНИЕ TYPEDEF И ОБЪЯВЛЕНИЙ ПСЕВДОНИМОВ template <typename

    T> class Widget{ public: typedef T&& RvalueRefToT; }; Widget<int&> w; typedef int& && RvalueRefToT; typedef int& RvalueRefToT;
  28. ПРЯМАЯ ПЕРЕДАЧА (PERFECT FORWARDING) struct X{ X(const int&, int&){} };

    struct W{ W(int&, int&){} }; struct Y{ Y(int&, const int&){} }; struct Z{ Z(const int&, const int&){} }; template <typename T, typename A1, typename A2> T* factory(A1& a1, A2& a2){ return new T(a1, a2); } int a = 4, b = 5; W* pw = factory<W>(a,b); // Ok. X* pw = factory<X>(2,b); // Error. Y* pw = factory<Y>(a,2); // Error. Z* pw = factory<Z>(2,2); // Error.
  29. ПРЯМАЯ ПЕРЕДАЧА (PERFECT FORWARDING) struct X{ X(const int&, int&){} };

    struct W{ W(int&, int&){} }; struct Y{ Y(int&, const int&){} }; struct Z{ Z(const int&, const int&){} }; template <typename T, typename A1, typename A2> T* factory(A1&& a1, A2&& a2){ return new T(std::forward<A1>(a1), std::forward<A2>(a2)); } int a = 4, b = 5; W* pw = factory<W>(a,b); // Ok. X* pw = factory<X>(2,b); // Ok. Y* pw = factory<Y>(a,2); // Ok. Z* pw = factory<Z>(2,2); // Ok.
  30. ПРЯМАЯ ПЕРЕДАЧА (PERFECT FORWARDING) template <typename T> void fwd(T&& param){

    f(std::forward<T>(param)); } // Variadic template template <typename... Ts> void fwd(Ts&&... params){ f(std::forward<Ts>(params)...); }