Захват этого и пересылка лямбды на другую лямбду

У меня есть случай, когда мне нужно передать лямбду в свободную функцию, которая затем вызовет эту лямбду в другой лямбде. Я привел пример ниже (ссылка):

#include <ranges>
#include <map>
#include <format>
#include <functional>
#include <iostream>

template<typename RANGE, typename LAM>
auto foo(RANGE range, LAM&& lambda) {
    return range | std::views::transform(
                [&](const auto& element) { return lambda(element); });
}


class boo {
public:
    int add {2};
    
    void call_foo(){
        std::vector<int> d { 2,3,4,5,6};
        auto f = foo(d, [this](int i) { 
            std::cout << add << std::endl;
            return add + i; 
            });
        std::cout << f[0] << f[1] << std::endl;
    };
};

int main() {
    std::cout << "HELLO" << std::endl;
    boo b;
    b.call_foo(); // is no longer valid inside the lambda 
}

Код segfaults здесь:

std::cout << add << std::endl;

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

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

Я не думаю, что проблема в захвате this. Ваша функция foo() принимает vector по значению, таким образом копируя его, а затем возвращает представление в эту копию, которая уже не существует к моменту чтения представления после выхода foo(). Для меня это пахнет неопределенным поведением. У вас все еще будет проблема, если вы измените foo(), чтобы вместо этого использовать vector по ссылке?

Remy Lebeau 01.03.2024 00:00
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Нет, не this становится висячим, а range и лямбда, к которой вы передаете foo, становятся висячими. Ссылка на range, содержащаяся внутри tranform_view, становится опасной, как только foo возвращается, а лямбда, к которой вы передаете foo, становится висячей в конце полного выражения. Вам нужна ссылка на d и зафиксируйте лямбду по значению во внутренней лямбде:

template <typename RANGE, typename LAM>
auto foo(RANGE& range, LAM&& lambda) {
//            ^
    return range | std::views::transform(
                       [lambda](const auto& element) { return lambda(element); });
//                      ^^^^^^
}

В противном случае возвращаемый transform_view попытается обработать range, который был копией d и вышел за пределы области действия. Вы хотите, чтобы он работал d — и то же самое касается лямбды, которую вы сейчас захватываете по ссылке. Эта лямбда перестает существовать в конце полного выражения, в котором вы вызываете foo. В этом случае вы должны сохранить его во внутренней лямбде или создать его перед вызовом foo, чтобы он сохранился после полного выражения, в котором вы вызываете foo().


С пересылкой лямбды:

template <typename RANGE, typename LAM>
auto foo(RANGE& range, LAM&& lambda) {
    return range | std::views::transform(
                       [lam = std::forward<LAM>(lambda)](const auto& element) {
                           return lam(element); 
                       });
}

Спасибо! это действительно работает. Когда я попытался реконструировать свой реальный вариант использования, я понял, что он все еще ошибочен. Я думаю, что дополнительный бит, который я добавил (который, как я изначально думал, не изменит область действия), является тем, что причиняет вред и, следовательно, вероятно, вызвано другой проблемой, поэтому я опубликовал отдельный билет: stackoverflow.com/questions/78085914/…

ATK 01.03.2024 07:35

@ATK О, это выглядит интересно! Я мало что знаю о библиотеке {fmt} и смогу посмотреть ее не сегодня, но, возможно, завтра.

Ted Lyngmo 01.03.2024 14:46

Другие вопросы по теме