将lambda传递给可变参数std :: function

时间:2019-01-17 04:34:13

标签: c++ templates c++17 variadic-templates std-function

我正在尝试使用用于处理它们的函数的类型信息从数组的元组中检索值。但是,在这种情况下,类型推导失败(部分原因是由于)需要为std::function的类型名使用标识结构。这里有办法还原扣吗?

#include <functional>
#include <iostream>
#include <tuple>

class comp_a
{
public:
    static const size_t id = 0;

    int val = 0;
};

class comp_b
{
public:
    static const size_t id = 1;

    int val = 0;
};

class comp_c
{
public:
    static const size_t id = 2;

    int val = 0;
};

template<size_t size, typename ... Cs>
struct storage
{    
    template<typename T> struct identity { using type = T; };

    template<typename ... Ts>
    void get(size_t index, typename identity<std::function<void(Ts& ...)>>::type f)
    {
        f(std::get<Ts::id>(components)[index] ...);
    }

    std::tuple<std::array<Cs, size> ...> components;
};

int32_t main()
{
    storage<20, comp_a, comp_b, comp_c> storage;

    storage.get(2, [](comp_a& a, comp_c& c) {              // Doesn't work
    // storage.get<comp_a, comp_c>(2, [](comp_a& a, comp_c& c) { // Works
        std::cout << a.val << " " << c.val << std::endl;
    });
}

我遇到过thisthis,它们看起来很相似,但是我相信我的情况有所不同,因为在检索所需值时,我需要可变参数类型才能访问其特征。就像在这些示例中一样,该函数的可变参数被视为void:

error: cannot convert 'main()::<lambda(comp_a&, comp_c&)>' to 'storage<20, comp_a, comp_b, comp_c>::identity<std::function<void()> >::type' {aka 'std::function<void()>'}

在这种情况下,扣除指南是否可行?类型信息似乎深藏在std::function的类型中,因此我不确定如何在指南中将其提取出来。

有一个在线示例here

3 个答案:

答案 0 :(得分:2)

您可以使用livenessProbe: httpGet: path: /health port: 80 readinessProbe: httpGet: path: /health port: 80 演绎指南技巧来引出参数,但您确实不想实际上创建 std::function。您想堆叠在lambda-land中。 std::function由于类型擦除而增加了开销和分配-但是您所做的事情实际上并不需要类型擦除所提供的好处。都是损失,没有胜利。 实际上没有做std::function

也就是说,您当然仍然需要参数。因此,您可以这样做:

std::function

基本上,我们采用一些可调用对象-然后从中推导出template <typename T> struct type { }; template <typename F> void get(size_t index, F f) { using function_type = decltype(std::function(f)); get_impl(index, f, type<function_type>{}); } 。这给了我们一些类型。在OP的特定示例中,该类型为std::function。然后,我们简单地将该类型转发给另一个函数-作为一个空对象。没有开销。重复一遍,我们实际上并没有创建std::function<void(comp_a&, comp_b&)>-我们只是传递其类型。

其他功能可以利用知道std::function知道的信息:

std::function

您需要在那里使用template <typename T> using uncvref_t = std::remove_cv_t<std::remove_reference_t<T>>; template <typename F, typename R, typename... Args> void get_impl(size_t index, F f, type_t<std::function<R(Args...)>>) { f(std::get<uncvref_t<Args>::id>(components)[index] ...); } 来处理uncvref_t可能是参考也不是 cv 合格的情况。

现在,这不适用于任何可调用对象。如果您传递通用lambda,则推论Args将失败。但是然后...仍然无法正常工作,因此看起来损失不大吗?

答案 1 :(得分:1)

我不确定identity结构的用途,但是删除它会给出更清晰的错误消息(模板推导失败)。

编译器无法从lambda派生std::function类型。为了证明这一点,可以编译以下内容:

storage.get(2, std::function<void(comp_a& a, comp_c& c)>([](comp_a& a, comp_c& c) {              // Doesn't work
    std::cout << a.val << " " << c.val << std::endl;
}));

因此,要使其正常工作,我们只需要帮助编译器派生类型即可。从http://www.cplusplus.com/forum/general/223816/借用以下作品:

namespace detail
{
    template < typename T > struct deduce_type;

    template < typename RETURN_TYPE, typename CLASS_TYPE, typename... ARGS >
    struct deduce_type< RETURN_TYPE(CLASS_TYPE::*)(ARGS...) const >
    {
        using type = std::function< RETURN_TYPE(ARGS...) >;
    };
}

template<size_t size, typename ... Cs>
struct storage
{
    template<typename ... Ts>
    void get(size_t index, typename std::function<void(Ts& ...)> f)
    {
        f(std::get<Ts::id>(components)[index] ...);
    }

    template<typename Lambda>
    void get(size_t index, Lambda l)
    {
        get( index, typename detail::deduce_type< decltype( &Lambda::operator() ) >::type( l ) );
    }

    std::tuple<std::array<Cs, size> ...> components;
};

答案 2 :(得分:1)

您标记了C ++ 17,因此可以使用std::function的演绎指南

因此,按照艾伦·伯特尔斯(Alan Birtles)的建议,您可以将lambda作为简单类型接收,将其转换为std::function(推导指南)并推论参数的类型。

类似

template<size_t size, typename ... Cs>
struct storage
{    
    template<typename ... Ts>
    void get(size_t index, std::function<void(Ts& ...)> f)
    { f(std::get<Ts::id>(components)[index] ...); }

    template <typename F>
    void get(size_t index, F f)
    { get(index, std::function{f}); }

    std::tuple<std::array<Cs, size> ...> components;
};