Захват std :: array в лямбде - PullRequest
       7

Захват std :: array в лямбде

3 голосов
/ 18 февраля 2020

Я пытался протестировать некоторые простые функции сортировки в стиле C. В программе драйвера я написал что-то вроде этого:

int main()
{
    std::array<int, 8> A = { 1, 0, 4, 5, 7, 2, 9, 3 };
    auto lambda = [&A](const std::function<void(int *A, int n)> &sorting_alg) {
        int n = A.size();
        sorting_alg(A.data(), n);
        std::cout << "=> ";
        print(A.data(), n);
        std::cout << std::endl;
    };

    auto do_bubble_sort = std::bind(lambda, bubble_sort);
    auto do_selection_sort  = std::bind(lambda, selection_sort);
    auto do_insertion_sort  = std::bind(lambda, insertion_sort);

    std::cout << "Bubble Sort :" << std::endl;
    do_bubble_sort();
    std::cout << "Selection Sort :" << std::endl;
    do_selection_sort();
    std::cout << "Insertion Sort :" << std::endl;
    do_insertion_sort();

    return 0;
}

У меня был код привязки, которому я мог передать A для копирования, но он ограничивает мою лямбду size=8, которую я хочу избегать. Можно ли добиться этого, не используя что-то вроде std::vector, et c? Как только я изменяю метод захвата A на захват значений, он больше не компилируется. Я хотел использовать копии для массива, чтобы проверить все функции сортировки. Почему я не могу захватить std::array по значению? Почему тогда определение размера работает для контрольного случая?

Ответы [ 3 ]

6 голосов
/ 18 февраля 2020

По умолчанию operator() лямбды равно const. Это означает, что вы не можете изменять какие-либо элементы лямбда (это захватывает). Когда вы захватываете по ссылке, это означает, что вы не можете изменить ссылку, чтобы ссылаться на что-то другое, но, поскольку вы уже не можете этого делать, это в основном ничего не значит. Когда вы захватываете по значению, это значение равно const, что означает, что вы больше не можете изменять, как того требует ваш метод сортировки (вам нужно data, чтобы вернуть неконстантный указатель).

Чтобы обойти это Вы можете использовать ключевое слово mutable, чтобы лямбда-символы operator() не const. Это будет выглядеть как

auto lambda = [A](const std::function<void(int *A, int n)> &sorting_alg) mutable {
    int n = A.size();
    sorting_alg(A.data(), n);
    std::cout << "=> ";
    print(A.data(), n);
    std::cout << std::endl;
};
3 голосов
/ 18 февраля 2020

Вы можете захватить по значению, но вы должны изменить лямбду на изменяемый.

Когда вы захватываете по значению, захваты являются постоянными по умолчанию. Вы можете пометить его как изменяемый или сделать копию локально:

auto lambda = [A](const std::function<void(int *A, int n)> &sorting_alg) {
    auto ALocal = A;
    int n = ALocal.size();
    sorting_alg(ALocal.data(), n);
    std::cout << "=> ";
    print(ALocal.data(), n);
    std::cout << std::endl;
};

Живой пример

Преимущество заключается в том, что лямбда может вызываться несколько раз.

Вы также можете сделать это с помощью захвата по ссылке.

В качестве альтернативы вы можете сделать лямбду изменчивой, но вызываемой только один раз:

auto lambda = [A](const std::function<void(int *A, int n)> &sorting_alg) mutable {
    int n = A.size();
    sorting_alg(A.data(), n);
    std::cout << "=> ";
    print(A.data(), n);
    std::cout << std::endl;
};
1 голос
/ 18 февраля 2020

Проблема в том, что operator() лямбда-функции * const -кавалифицирован по умолчанию. Это означает, что значения, захваченные значением, * const -квалифицированы в лямбда-теле. В вашем коде вы пытаетесь вызвать std::function с не const указателем на int, инициализированным из захваченного массива, который является const.

. Чтобы это исправить, вам нужно пометить лямбда как mutable, так что сгенерированный operator() больше не является const -качественным:

auto lambda = [A](const std::function<void(int *A, int n)> &sorting_alg) mutable {
    ...
}
...