Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - страница 22

стр.

, выводится из их инициализатора, так что они обязаны быть инициализированными. Это значит — прощай проблема неинициализированных переменных:

>int x1;      // Потенциально неинициализированная переменная

>auto x2;     // Ошибка! Требуется инициализатор

>auto x3 = 0; // Все отлично, переменная x корректно определена

Нет проблем и с объявлением локальной переменной, значением которой является разыменование итератора:

>template // Все, как и ранее

>void dwim(It b, It e) {

> while (b != e) {

>  auto currValue = *b;

> }

>}

А поскольку >auto использует вывод типов (см. раздел 1.2), он может представлять типы, известные только компиляторам:

>auto derefUPLess =                     // Функция сравнения

> [](const std::unique_ptr& p1, // объектов Widget, на

>    const std::unique_ptr& p2) // которые указывают

>    { return *p1 < *p2; );             // std::unique_ptr

Просто круто! В С++14 все еще круче, потому что параметры лямбда-выражений также могут включать >auto:

>auto derefLess =           // Функция сравнения в С++14,

> [](const auto& p1,        // для значений, на которые

>    const auto& p2)        // указывает что угодно

>    { return *p1 < *p2; }; // указателеобразное

Несмотря на всю крутость вы, вероятно, думаете, что можно обойтись и без >auto для объявления переменной, которая хранит лямбда-выражение, поскольку мы можем использовать объект >std::function. Это так, можем, но, возможно, это не то, что вы на самом деле подразумеваете. А может быть, вы сейчас думаете “А что это такое — объект >std::function?” Давайте разбираться.

>std::function — шаблон стандартной библиотеки С++11, который обобщает идею указателя на функцию. В то время как указатели на функции могут указывать только на функции, объект >std::function может ссылаться на любой вызываемый объект, т.e. на все, что может быть вызвано как функция. Так же как при создании указателя на функцию вы должны указать тип функции, на которую указываете (т.e. сигнатуру функции, на которую хотите указать), вы должны указать тип функции, на которую будет ссылаться создаваемый объект >std::function. Это делается с помощью параметра шаблона >std::function. Например, для объявления объекта >std::function с именем >func, который может ссылаться на любой вызываемый объект, действующий так, как если бы его сигнатура была

>bool(const std::unique_ptr&, // Сигнатура C++11 для

>     const std::unique_ptr&) // функции сравнения

>                                     // std::unique_ptr

следует написать следующее:

>std::function<bool(const std::unique_ptr&,

>                   const std::unique_ptr&)> func;

Поскольку лямбда-выражения дают вызываемые объекты, замыкания могут храниться в объектах >std::function. Это означает, что можно объявить С++11-версию >derefUPLess без применения >auto следующим образом:

>std::function&,

>                   const std::unique_ptr&)>

> derefUPLess = [](const std::unique_ptr& p1,

>                  const std::unique_ptr& p2)

>                  { return *p1 < *p2; };

Важно понимать, что, даже если оставить в стороне синтаксическую многословность и необходимость повторения типов параметров, использование >std::function — не то же самое, что использование >auto. Переменная, объявленная с использованием >auto и хранящая замыкание, имеет тот же тип, что и замыкание, и как таковая использует только то количество памяти, которое требуется замыканию. Тип переменной, объявленной как >std::function и хранящей замыкание, представляет собой конкретизацию шаблона >std::function, которая имеет фиксированный размер для каждой заданной сигнатуры. Этот размер может быть не адекватным для замыкания, которое требуется хранить, и в этом случае конструктор >std::function будет выделять для хранения замыкания динамическую память. В результате объект >std::function использует больше памяти, чем объект, объявленный с помощью >auto. Кроме того, из-за деталей реализации это ограничивает возможности встраивания и приводит к косвенным вызовам функции, так что вызовы замыкания через объект