Позвольте мне сказать, что я хочу изменить последовательность элементов. Сначала я должен определить функцию.

Одно определение:

int main (void) {
 // sequence
     void reverse (vector<int> &arr) {
         int i = 0, j = arr.size() — 1;
         while (i < j) {
             int temp = arr[i];
             arr[i++] = arr[j];
             arr[j--] = temp;
         }
     }
 // calls reverse
 return 0;
}

Но вы не можете сделать это в C++. Ваш компилятор жалуется на это.

file.cpp:9:35: error: function definition is not allowed here
 void reverse(vector<int> & arr) {
 ^
file.cpp:21:3: error: no matching function for call to ‘reverse’
 reverse(arr);
 ^~~~~~~

Вы не можете определить функцию внутри другой функции.

Таким образом, единственный подход будет заключаться в том, чтобы определить его в другом месте.

void reverse (vector<int> &arr) {
    int i = 0, j = arr.size() — 1;
    while (i < j) {
        int temp = arr[i];
        arr[i++] = arr[j];
        arr[j--] = temp;
    }
}
int main (void) {
    // sequence
    // calls reverse
    return 0;
}

Это просто работает.

[monster@monster tmp]$ clang++ file.cpp && ./a.out
9 8 7 6 5 4 3 2 1 
[monster@monster tmp]$

Было бы лучше быть модульным и написать функцию подкачки. Обратите внимание, что мы не можем написать функцию swap внутри обратной функции, так как это не разрешено. Стандарт говорит, что предварительное объявление является обязательным, но в современных компиляторах оно работает и без него.

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
void reverse (vector<int> & arr) {
    int i = 0, j = arr.size() — 1;
    while (i < j) {
        swap(arr[i++], arr[j--]);
    }
}
// main here

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

Короче говоря, мы можем. У нас есть лямбда-выражения C++.

int main(void) {
    // sequence
    auto swap = [](int *a, int *b) {
        int temp = *a;
        *a = *b;
        *b = temp;
    };
 
    auto reverse = [](vector<int> & arr) {
        int i = 0, j = arr.size() — 1;
        while (i < j)
            swap(arr[i++], arr[j--]);
    };
    // calls reverse
}

Мы тоже можем это сделать.

auto reverse = [](vector<int> &arr) {
    auto swap = [](int *a, int *b) {
        int temp = *a;
        *a = *b;
        *b = temp;
    };
    int i = 0, j = arr.size() — 1;
        while (i < j)
            swap(arr[i++], arr[j--]);
};

Переменная reverse получает тип данных function<void(vector<int> &)>, а переменная swap получает тип данных function<void(int *, int *)>.

Что насчет рекурсии?

Рекурсия немного сложна. Следующий код не работает.

auto factorial = [](int n) {
    if (n <= 1) return 1;
    return n * factorial(n — 1);
};

Не работает по разным причинам.

Одна из причин заключается в том, что тип возвращаемого значения вообще неизвестен. auto не может определить тип. мы знаем, что делает оператор if, возвращает 1, но мы не уверены, что сделает оператор else. По крайней мере, наш компилятор не знает.

file.cpp:9:19: error: variable ‘factorial’ declared with deduced type ‘auto’
 cannot appear in its own initializer
 return n * factorial(n — 1);
 ^
1 error generated.

Другая причина в том, что лямбда-функция factorial вообще не захвачена. Чтобы захватить его, нам просто нужно передать то же самое в предложении захвата, [&]. & говорит передавать все как ссылку.

file.cpp:9:19: error: variable ‘factorial’ cannot be implicitly captured in a
 lambda with no capture-default specified
 return n * factorial(n — 1);
 ^
file.cpp:7:22: note: ‘factorial’ declared here
 function<int(int)> factorial = [](int n) {
 ^
file.cpp:7:34: note: lambda expression begins here
 function<int(int)> factorial = [](int n) {

Наконец, это будет наше определение.

function<int(int)> factorial = [&](int n) {
    if (n <= 1) return 1;
    return n * factorial(n — 1);
};

Единственное, что мы выиграли от лямбда-выражений, это то, что их можно определить где угодно.

Хотя это неправда. C++14 добавляет к этому гораздо больше.

В C++14 даже параметры можно определить с помощью auto.

auto lambda = [](auto x, auto y) {
    return x + y;
};

Приведенный выше код будет эквивалентен

struct unnamed_lambda {
    template<typename T, typename U>
       auto operator()(T x, U y) const {
           return x + y;
       }
    };
};
auto lambda = unnamed_lambda{};

Фактическая проблема с лямбда-выражениями - это рекурсии. Как было сказано ранее, мы можем определить тип function<> и создать лямбда-функцию, но проблема с использованием типа function<> заключается в том, что std::function имеет проблемы с производительностью, поскольку он выполняет выделение кучи.

Таким образом, рекурсии в лямбда-выражениях применимы только тогда, когда вы определяете тип function<>. Так было до C++11. Но поскольку C++14 позволяет параметрам иметь объявление auto, мы можем передать саму лямбда-функцию в качестве аргумента.

auto factorial = [](auto &&self, auto n) {
    if (n <= 1) return 1;
    return n * self(self, n — 1);
};
// usage: 
factorial(factorial, 5);

&& является ссылкой RValue. Мы можем игнорировать его, но поскольку мы его не меняем, мы можем просто передать его как ссылку. И смотрите, нам не нужно захватывать саму функцию для рекурсии, так как функция захватывается как аргумент.