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

Скотт Мейерс

Ладно, третья попытка. Попробую объявить локальную переменную, тип которой такой же, как у лямбда-выражения. Но его тип известен только компилятору. #@$! (Это становится привычкой…)

Да что же это такое — никакого удовольствия от программирования на С++! Так не должно быть. И не будет! Мы дождались С++11, в котором все эти проблемы решены с помощью ключевого слова auto. Тип переменных, объявленных как auto, выводится из их инициализатора, так что они обязаны быть инициализированными. Это значит — прощай проблема неинициализированных переменных:

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

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

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

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

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

void dwim(It b, It e) {

 while (b != e) {

  auto currValue = *b;

 }

}

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

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

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

    const std::unique_ptr<Widget>& 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<Widget>&, // Сигнатура C++11 для

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

                                     // std::unique_ptr<Widget>

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