如何通过 std::apply 调用具有定义的第一个参数的可变参数模板函数?

How to call variadic template function with defined first parameter through std::apply?

我对参数包比较陌生,但我想构建一个 class 来接收参数包,收集其中包含的值并将它们启动到另一个函数中。

我有一个 class,它有可变参数模板和一个赋值函数。函数正在从 VARIANT 结构中获取参数。

解包函数:

 template <typename...Args>
    constexpr void ValuesFromValuesVariant(VARIANT variant, Args&...args)
    {
        static std::size_t nArgs = sizeof...(Args);
        if (!nArgs) return;
        auto vArray = GetArray(variant, nArgs);

        auto i = 0u;
        GetNextItem(vArray, i, args);
    }
使用它的

Class:

template <class ...Args>
class Receiver
{
    using SendFunction = std::function<HRESULT(Args...)>;
    using InterruptFunc = std::function<HRESULT()>;
    ...
    virtual HRESULT OnSend(VARIANT params)
    {
        //Here I'd like to call it with a VARIANT, but I can't for some reason
        std::apply(ValuesFromValuesVariant, params, std::tie(arguments)); //std::tie to get refs& to the arguments

        HRESULT hr = std::apply(m_Function, arguments);
        return hr;
    }
private:
    InterruptFunc m_InterruptFunc;
    SendFunction m_Function;
private:
    std::tuple< Args... > arguments;
};

我试过 std::bind 第一个函数参数,但它不能自动预测它接受的参数, VariantCommunication::ValuesFromValuesVariant<Args> 说明符无效。

要将 apply 与元组之外的参数一起使用,您可以在函子中捕获它:

std::apply([&](auto&... args){ ValuesFromValuesVariant(params, args...); }, arguments);

或将额外参数连接到元组:

std::apply(&ValuesFromValuesVariant, std::tuple_cat(std::tie(params), arguments));

因为ValuesFromValuesVariant通过左值引用获取它的参数,首先你需要从arguments创建左值引用的元组。然后你可以使用 tuple_cat 连接从 params 和前一个创建的元组:

它可能看起来像:

virtual HRESULT OnSend(VARIANT params)
{
    // [1] make tuple of references to arguments
    auto makeRef = [](auto & ... args){ return std::forward_as_tuple(args...); };
    auto t1 = std::apply( makeRef , arguments);

    // [2] make tuple from params
    auto t2 = std::forward_as_tuple(params);
    std::apply(ValuesFromValuesVariant<Args...>, std::tuple_cat( t1, t2 ) );

    HRESULT hr{1};
    return hr;
}

Here is minimal demo

首先,std::tie(arguments) 不会创建 std::tuplearguments 元素的引用。它将做的是创建一个 std::tuple,它具有一个引用 arguments 的单个元素。要获取对元组的引用的元组,需要一个适配器,如

template<typename... Elems, std::size_t... Indicies>
auto make_reference_tuple_impl(std::tuple<Elems...>& tuple, std::index_sequence<Indicies...> indices)
{
    return std::tuple<Elems&...>{std::get<Indicies>(tuple)...};
}

template<typename... Elems>
auto make_reference_tuple(std::tuple<Elems...>& tuple)
{
    return make_reference_tuple_impl(tuple, std::make_index_sequence<sizeof...(Elems)>{});
}

然后你需要做的是将 paramsstd::tie 一起变成引用的元组,然后你可以将这些元组与 std::tuple_cat 组合在一起。您还需要将 ValuesFromValuesVariant 包装在 lambda 中,因为您可以将函数模板传递给 std::apply。把所有这些放在一起会给你的代码看起来像

virtual HRESULT OnSend(VARIANT params)
{
    auto param_ref = std::tie(params);
    auto arg_ref = make_reference_tuple(arguments);
    auto concat = std::tuple_cat(param_ref, arg_ref);
    std::apply([](auto...& args){ValuesFromValuesVariant(args);}, concat);

    HRESULT hr = std::apply(m_Function, arguments);
    return hr;
}