【问题标题】:Dividing variadic parameters in half [duplicate]将可变参数分成两半
【发布时间】:2021-04-23 14:21:10
【问题描述】:

我写了一个方便的函数,它获取一些参数然后发送它们 到正确的功能:

void combine(int a, int b, double one, double two) {
    funA(a,b);
    funB(one, two);
}

现在我想再做一次,但我的combine 函数和funA-funB 对都使用可变模板参数:

//somewhere earlier in the code 
template<class U, class T> funA(U par1, T par2) {...}
template<class X, class Y> funB(X par1, Y par2) {...}

template<class ...ParamA, class ...ParamB>
void combine(ParamA...packA, ParamB...packB) {
     funA(packA...);
     funB(packB...);
}

这当然行不通,因为我需要一些方法将参数列表分成两半。 但更有趣的是,当我尝试使用类似的调用编译上述代码时 combine(10, 'a', 12.0, true)我收到这个错误:

In instantiation of ‘void combine(ParamA ..., ParamB ...) [with ParamA = {}; ParamB = {int, char, double, bool}]’:
...
error: no matching function for call to ‘funA()’
....
error: no matching function for call to ‘funB(int&, char&, double&, bool&)’

这表明ParamB“吃了”所有的参数列表。

所以,我的问题是:有没有办法将带有变量模板列表的函数的参数列表分成两半?。如果没有,我还能如何编写我的 combine 函数?

【问题讨论】:

    标签: c++ variadic-templates


    【解决方案1】:
    #include <tuple>
    #include <utility>
    
    #include <iostream>
    #include <memory>
    
    //somewhere earlier in the code 
    template<class U, class T> void funA(U par1, T par2) {
        std::cout<<par1 <<' '<< par2 <<'\n';
    }
    // Use move-only type to check forwarding
    template<class X> void funB(X par1, std::unique_ptr<int> par2) {
        std::cout<<par1 <<' '<< *par2 <<'\n';
    }
    
    // Call fnc with a subset of arguments, more general than we need for the partitioning.
    template<typename Fnc,std::size_t...Is, typename...Args>
    auto call_sub(Fnc fnc, std::index_sequence<Is...>, Args&&...args){
       
        auto tup = std::forward_as_tuple(std::forward<Args>(args)...);
        // Extract relevant indices.
        return fnc(std::get<Is>(std::move(tup))...);
    }
    
    // Shift index sequences
    template< std::size_t shift_amount,std::size_t...Is>
    constexpr auto shift_sequence( std::index_sequence<Is...>){
        return  std::index_sequence<shift_amount +Is...>{};
    }
    
    template<class ...Params>
    void combine(Params&&...pack) {
        static_assert(sizeof...(pack) % 2 == 0);
        constexpr std::size_t half = sizeof...(pack) /2;
    
        constexpr std::make_index_sequence<half> first_half;
        constexpr auto second_half = shift_sequence<half>(first_half);
        // Lambda must be used because funA nor funB are not functions, they are templates which cannot be passed around.
        call_sub([](auto&&...args){return funA(std::forward<decltype(args)>(args)...);},
                  first_half, std::forward<Params>(pack)...);
        call_sub([](auto&&...args){return funB(std::forward<decltype(args)>(args)...);},
                  second_half, std::forward<Params>(pack)...);
    
    
    }
    int main (int argc, char *argv[])
    {
        combine(1,2,3,std::make_unique<int>(10));
    }
    

    输出:

    1 2
    3 10
    

    Live Godbolt demo

    【讨论】:

      猜你喜欢
      • 2013-05-25
      • 2013-12-06
      • 2012-05-26
      • 2020-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-02
      • 2020-07-20
      相关资源
      最近更新 更多