Подтвердить что ты не робот

Параметр/тип хранения для С++ 11 lambda

В С++ 11, как вы объявляете функцию, которая принимает лямбда-выражение в качестве аргумента? Я могу найти много ресурсов в Интернете для объявления лямбда или принятия их в качестве параметров шаблона, но то, что я действительно хотел бы сделать, - это использовать lambdas как простые для декларирования обработчики обратного вызова, похожие на то, что стало возможным благодаря закрытию в JavaScript и кодовых блоках в Objective-C.

По существу, классическая конструкция С++, которую я хочу заменить лямбдой, выглядит примерно так:

class MyCallback {
public:
    virtual ~MyCallback() {}
    virtual void operator(int arg) = 0;
};

void registerCallback(const std::shared_ptr<MyCallback> &);

void foo(void) {
    int a, b, c;
    class LocalCallback: public MyCallback {
        int a, b, c;
    public:
        LocalCallback(int a, int b, int c): a(a), b(b), c(c) {}
        void operator(int arg) { std::cout << (a+b+c)*arg << std::endl; }
    };
    registerCallback(std::shared_ptr<MyCallback>(new LocalCallback(a,b,c)));
}

который был бы упрощен в:

void registerCallback(/* WHAT GOES HERE? */);

void foo(void) {
    int a, b, c;
    registerCallback([=](int arg){std::cout << (a+b+c)*arg << std::endl; })
}

Итак, что там, где я написал /* WHAT GOES HERE? */?

РЕДАКТИРОВАТЬ: Это предназначено для хранения обратного вызова, который будет вызван позже, а не для его немедленного потребления и вызова.

4b9b3361

Ответ 1

Обычно const std::function<void(int)> & или std::function<void(int)>.

Я не уверен, в чем заключается вердикт, следует ли передавать std::function по ссылке const или по значению. Вероятно, по достоинству это прекрасно, тем более, что вы все равно скопируете его.

Если это не ясно в середине всего этого синтаксиса, void(int) - это тип функции, а std::function<T> означает приблизительно "функтор с той же сигнатурой, что и функции типа T".

У самих лямбдов есть анонимные типы. Невозможно назвать тип вашего лямбда-выражения, а типы разных лямбда-выражений с одной и той же сигнатурой различны:

auto foo = [=](int arg){std::cout << (a+b+c)*arg << std::endl; };
auto bar = [=](int arg){std::cout << (a+b+c)*arg << std::endl; };
// foo and bar have different types, accessible as decltype(foo), decltype(bar)

Следовательно, необходимо, чтобы std::function, который в основном представляет собой обертку, стирающую тип, чтобы собрать вместе разные функторы с одной и той же сигнатурой в общий тип. Это мост между статическим полиморфизмом с шаблонами и динамический полиморфизм, который вам нужен, если вы хотите зарегистрировать обратный вызов, сохранить его позже и затем вызвать его, не "вспомнив" оригинальный тип.

Ответ 2

void registerCallback(const std::function<void(int)>& callback);

Ответ 3

Рассмотрим использование шаблона функции. Существует множество веских причин, таких как улучшение поведения при перегрузке (перегрузка на std::function является болезненной):

template<typename Functor>
void registerCallback(Functor&& functor);

(Вы также можете принять параметр Functor functor, что не слишком важно.)

Если код должен, например, затем сохраните functor, тогда это, вероятно, будет храниться внутри std::function. Где вы хотите избежать std::function находится в функциональных параметрах.