2

立即回拨

考虑以下示例:

template <typename lambda> void call_me_back(const lambda & callback)
{
    // Very complicated calculation to find the meaning of everything

    callback(42);
}

int main()
{
    call_me_back([](const int & value)
    {
        std :: cout << value << std :: endl;
    });
}

在这里,我提供call_me_back了一个接受int. 经过长时间的计算后call_me_back调用,并将其打印出来。轻松如蛋糕。callbackcallback

现在,假设根据执行情况,call_me_back需要callback使用 anint或其他类型调用。我们可以将前面的示例编辑为

template <typename lambda> void call_me_back(const lambda & callback)
{
    // Very complicated calculation to find the meaning of everything

    if(rand() % 2)
        callback(42);
    else
        callback("Kill all the humans.");
}

int main()
{
    call_me_back([](const auto & value)
    {
        std :: cout << value << std :: endl;
    });
}

到目前为止,一切都很好。现在可以根据其类型callback进行各种技巧和处理。value

延迟回调

现在,回到第一个例子。说call_me_back没有准备好立即callbackint. 但是,它可以做的是存储某处callback,然后再调用它。

例如:

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

template <typename lambda> void call_me_back(const lambda & callback)
{
    the_callback = callback;
}

void ready_to_answer()
{
    the_callback(42);
}

int main()
{
    call_me_back([](const auto & value)
    {
        std :: cout << value << std :: endl;
    });
}

现在,不是立即调用,而是callback存储call_me_backcallback一个std :: function <void(const int &)>对象中(我知道,它在全局范围内,请耐心等待)。然后可能会发生很多事情,并且在某些时候有人可以调用ready_to_answer,它会检索先前存储的回调并调用它。例如,ready_to_answer可以由另一个线程调用,但是有很多原因需要这样的范例,回调可以存储并稍后调用。

问题

如果我想实现第二个示例,但使用延迟回调怎么办?. 我似乎无法理解这一点。

我可以想象这std :: function是使用接受某种特定类型的虚拟呼叫运算符实现的。然后std :: function包装一个指向模板包装类的指针/引用,该模板包装类存储实际的 lambda,并通过将其参数转发给它正在存储的 lambda 来实现调用运算符。好,易于。但我不能有模板虚拟方法!

我试过想出各种解决方案,但我找不到任何可以合理工作的方法。这真的不可能吗?是否不可能有一些外部提供的 lambda 接受const auto &存储在某处的参数,然后再调用它?

4

3 回答 3

7

你是对的,无限的类型集是不可能的,但如果你事先知道所有类型,你就可以做到:

std :: function <void(const std :: variant<int, std :: string> &)> the_callback;

template <typename lambda> void call_me_back(const lambda & callback)
{
    the_callback = [callback](const auto & arg)
    {
        std :: visit(callback, arg);
    };
}

template <typename T> void ready_to_answer(const T & x)
{
    the_callback(x);
}

int main()
{
    call_me_back([](const auto & value)
    {
        std :: cout << value << std :: endl;
    });

    if (std :: rand() % 2)
    {
        ready_to_answer(42);
    }
    else
    {
        ready_to_answer("Hi!");
    }
}
于 2017-07-09T17:41:10.023 回答
1

在您的第二个示例中,您没有传递函数,而是使用两个方法传递对匿名 lambda 对象的引用,operator()(int)并且operator(char const (&)[..]). 因此,要存储这种回调,您需要复制 lambda 对象或存储特定的回调方法,即使用多个::std::function具有相应签名的回调方法。实际上,为这两种情况显式传递两个回调会更清楚。

std::function< void (int) > delalyed_int_cb;
std::function< void (const char *) > delalyed_str_cb;

template< typename callable_taking_int, typename callable_taking_string > void
call_me_back(callable_taking_int && int_cb, callable_taking_string && str_cb)
{
    delalyed_int_cb = std::forward< callable_taking_int >(int_cb);
    delalyed_str_cb = std::forward< callable_taking_str >(str_cb);
    ...
}

void
ready_to_answer()
{
    if(rand() % 2)
    {
        delalyed_int_cb(42);
    }
    else
    {
        delalyed_str_cb("Kill all the humans.");
    }
}
于 2017-07-09T17:42:27.593 回答
1

您可以实现一个为您存储 lambda 的类模板。

#include<iostream>

template<typename L>
struct Telephone
{
    L l;

    Telephone(L l) : l{std::move(l)} {}

    template<typename... Args>
    decltype(auto) ready_to_answer(Args&&... args)
    {
        return l(std::forward<Args>(args)...);   
    }
};

template<typename L>
auto call_me_back(L&& l)
{
    return Telephone<std::decay_t<L>>(std::forward<L>(l));
}

int main()
{
    auto telephone = call_me_back([](auto x){ std::cout << x << std::endl; });
    telephone.ready_to_answer(42);
    telephone.ready_to_answer("Kill all the humans.");
}

这具有在 lambda 上模板化的缺点,Telephone并且每个 lambda 将是不同的类型。

如果您事先知道函数签名的样子,则可以Telephone从公共基类继承并具有虚拟的非模板方法。

进行一些自动构建和管理,Telephone您基本上实现了另一个std::function,但使用自定义签名

struct Phone
{
    // all Telephone<L> inherits from Phone and have these methods

    virtual void ready_to_answer(int) = 0;
    virtual void ready_to_answer(const char*) = 0;
};

struct Spouse
{
    std::unique_ptr<Phone> phone;

    template<typename L>
    Spouse(L&& l) : phone{ new Telephone<std::decay_t<L>>{std::forward<L>(l)} } {}

    void ready_to_answer(int i) { phone->ready_to_answer(i); }
    void ready_to_answer(const char* str) { phone->ready_to_answer(str); }
};

编辑:我继续实施了一个增强版本,std::function它接受任意数量的签名,所以你可以写

function<void (int), void (std::string)> callback =
    [](auto x){std::cout << x << std::endl;};

callback(42);
callback("Kill all humans!");

居住

可以说实现不是微不足道的,这里解释的太多了。

于 2017-07-09T17:52:05.927 回答