tuple和vector解包当做参数传入到函数中demo

tuple和vector解包当做参数传入到函数中

在C++中,有一些情况下,我们需要将tuple或者vector中的值当做参数传递到一个函数当中,传统的做法如下:

std::vector<int> v;
call(v[0], v[1], v[2]);
auto r = std::make_tuple(1, 2.1, 1ull)
call(std::get<0>(r), std::get<1>(r), std::get<2>(r));

我们可以通过如下的方式,将tuple解包传入到函数当中:

#include <iostream>
#include <tuple>

template<size_t ...> struct index_sequence  { };
template<size_t N, size_t ...S> struct make_index_sequence_helper : make_index_sequence_helper <N - 1, N - 1, S...> { };
template<size_t ...S> struct make_index_sequence_helper <0, S...> { typedef index_sequence<S...> type; };
template<size_t N> using make_index_sequence = typename make_index_sequence_helper<N>::type;

void test(int a, double b) {
    std::cout << a << " " << b << std::endl;
}

template<typename F, size_t ...i, typename T>
void callFuncHelper(F f, index_sequence<i...>, T&& t) {
    f(std::get<i>(t)...);
}

template<typename F, typename... T>
void callFunc(F f, const std::tuple<T...>& t) {
    callFuncHelper(f, make_index_sequence<sizeof...(T)>(), t);
}

int main() {
    auto inp = std::make_tuple(0, 1.23);
    callFunc(test, inp);
}

前面第一段通过继承机制提供了一个make_index_sequence的模板类,C++14中可以直接使用这个模板类,c++11中需要自己来实现,它是通过继承的方案实现的:

假如N = 4, 那么:

make_index_sequence_helper<4>: make_index_sequence_helper<3, 3>;
make_index_sequence_helper<4, 4>: make_index_sequence_helper<2, 2, 3>;
make_index_sequence_helper<2, 2, 3>:make_index_sequence_helper<1, 1, 2, 3>;
make_index_sequence_helper<1, 1, 2, 3>:make_index_sequence_helper<0, 0, 1, 2, 3>;
make_index_sequence_helper<0, 0, 1, 2, 3> {
    typedef index_sequence<0, 1, 2, 3> type;
}

test是一个测试函数,callFunc需要两个输入分别是被调用的函数f和一个tuple的参数t,它负责对函数进行调用。它计算出tuple中的元素个数,构造出一个index_sequence传递到callFuncHelper中,由该函数调用std::get对tuple进行解包


我们可以通过如下的方式,将vector解包传入到函数当中

#include <iostream>
#include <vector>

void test(int a, int b) {
    std::cout << a << " " << b << std::endl;
}

template <size_t N, typename F, typename T, typename...Args>
typename std::enable_if<N == 0>::type
callFunc(F f, const std::vector<T>& v, Args&&... args) {
    f(std::forward<Args>(args)...);
}

template <size_t N, typename F, typename T, typename...Args>
typename std::enable_if<N != 0>::type
callFunc(F f, const std::vector<T>& v, Args&&... args) {
    callFunc<N - 1>(test, v, v[N - 1], std::forward<Args>(args)...);
}

int main() {
    std::vector<int> inp{1, 2};
    callFunc<2>(test, inp);
}

test还是我们要测试的函数,前面的callFunc利用了sfinae准则,当N为0的时候,只能对第一个函数进行特化;当N不为0的时候,只能对第二个函数进行特化;通过这种方式,可以将vector进行解包当做参数传到func当中。(对enable_if有疑问的,可以参考 https://en.cppreference.com/w/cpp/types/enable_if)

当N = n时,解析第n-1个参数,并放在上一轮解析的参数之前,传给下一轮;当N = 0时,对传入的函数进行调用。

需要注意的是,vector的长度必须是在编译期就已知的,或者我们只想取该vector的前某几个参数传到对应的函数中。

当然如果你觉得这个示例中的<2>起来看着不舒服的话,也可以写一个辅助函数,获取test函数的参数数量,然后将这个数量当做模板参数传到callFunc中。


最后,根据上面所述的原理,提供一种tuple转vector的方式,并且允许转换的过程中进行一系列的转化操作。若编译不成功,需要加上-std=c++14:

//-std=c++14
#include <iostream>
#include <tuple>
#include <utility>
#include <vector>

template <class T>
T transfer(T a, T b)
{
    return a * b;
}

template <size_t... Is, typename... Args>
std::vector<int> helper(std::index_sequence<Is...>, const std::tuple<Args...>& tp)
{
    return std::vector<int> { transfer(std::get<Is>(tp), 3)... };
}

template <typename... Args>
std::vector<int> tupleToList(const std::tuple<Args...>& tp)
{
    return helper(std::make_index_sequence<sizeof...(Args)>(), tp);
}

int main()
{
    auto tp = std::make_tuple(1, 2, 3);
    auto r = tupleToList(tp);
    for (auto i : r) {
        std::cout << i << std::endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值