c++17中的apply和make_from_tuple

一、tuple的应用

前面对std::tuple进行了基础的学习,但是如何更好的应用std::tuple,在STL库里其实有更多的使用方式。在函数参数应用std::tuple的场景中,如何对std::tuple的数据进行操作,也即函数参数与其进行转换的方式。这里就得提到std::apply和std::make_from_tuple两个函数。前者提供了将std::tuple转化为参数并调用相关的函数。而后者功能是类似,只是处理对象时对构造函数的处理,非常有用。下面就分析一下这两个函数。

二、std::apply

先看一下定义:

template <class F, class Tuple>
constexpr decltype(auto) apply(F&& f, Tuple&& t);

看第一个模板形参,class F,一般来说,这个代表着函数。这里F可以表示函数,Lambda与表达式,仿函数及函数指针等。
在官网的文档中,给出一个可能的实现,也就是要实现一个自己的sequence,这就有点麻烦了。换句话说,如果没有这个std::apply,自己实现一个类似的功能,有点小复杂。在上面说了,这个函数的作用是把tuple自动拆封,对应成相关的参数,看一下例程:

#include <iostream>
#include <tuple>


int Add(int a, int b, int c,int d,int e,int f) 
{
    return a + b + c + d + e + f;
}

class Example 
{
public:
    int Add(int a, int b, int c, int d, int e, int f) 
    {
        return a + b + c + d + e + f;
    }
};

template <typename... T>
void TestVT(T... args) 
{
    std::cout << sizeof... (args) << std::endl;
    (std::cout << ... << args) << std::endl;
}
int main() {
    //普通函数
    std::tuple t(1,2,3,4,5,6);
    int r = std::apply(Add, std::move(t));
    std::cout << "result is:"<<r << std::endl;
    
    //类成员函数
    std::tuple t1(Example(),1,2,3,4,5,6);
    r = std::apply(&Example::Add,std::move(t1));
    std::cout << "result is:" << r << std::endl;

    //变参和Lambda
    //std::apply(TestVT,std::move(t));//错误
    std::apply([](auto &&... args) {return TestVT(args...); }, std::move(t));//利用新标准中Lambda表达式的auto实现调用

    return 0;
}

结果是:

result is:21
result is:21
6
123456

之所以给这么一个挺长参数的函数,目的就是为了更形象的说明这个函数。再看一下cppreference的例子:

#include <iostream>
#include <tuple>
#include <utility>
 
int add(int first, int second) { return first + second; }
 
template<typename T>
T add_generic(T first, T second) { return first + second; }
 
auto add_lambda = [](auto first, auto second) { return first + second; };
 
template<typename... Ts>
std::ostream& operator<<(std::ostream& os, std::tuple<Ts...> const& theTuple)
{
    std::apply
    (
        [&os](Ts const&... tupleArgs)
        {
            os << '[';
            std::size_t n{0};
            ((os << tupleArgs << (++n != sizeof...(Ts) ? ", " : "")), ...);
            os << ']';
        }, theTuple
    );
    return os;
}
 
int main()
{
    // OK
    std::cout << std::apply(add, std::pair(1, 2)) << '\n';
 
    // 错误:无法推导函数类型
    // std::cout << std::apply(add_generic, std::make_pair(2.0f, 3.0f)) << '\n'; 
 
    // OK
    std::cout << std::apply(add_lambda, std::pair(2.0f, 3.0f)) << '\n'; 
 
    // 进阶示例
    std::tuple myTuple(25, "Hello", 9.31f, 'c');
    std::cout << myTuple << '\n';
}

结果是:

3
5
[25, Hello, 9.31, c]

在这个网页上有句说明:元组不必是 std::tuple ,可以为任何支持 std::get 和 std::tuple_size 的类型所替代;特别是可以用 std::array 和 std::pair 。对应着例子就应该明白了。

三、std::make_from_tuple

这个用来处理对象的,也就是说有构造函数,非平凡的。看一下例子:

class Foo 
{
public:
    Foo(int first, float second, int third) 
    {
        std::cout << first << ", " << second << ", " << third << std::endl;
    }
    int operator ()()
    {
        std::cout << "is call" << std::endl;
        return 1;
    }
};

void TestMake()
{
    auto tuple = std::make_tuple(42, 3.14f, 0);
    auto a = std::make_from_tuple<Foo>(std::move(tuple));
    std::cout << "return is:" << a() << std::endl;
}
int main() 
{
    TestMake();
    return 0;
}

多么简单,而如果没有这个函数,就得自己封装并解析std::tuple中的参数,转化成对象。这就是利用上面的变参模板函数和Lambda表达式了。

class Foo 
{
public:
    Foo(int a, double b, const std::string& c,int d) : a_(a), b_(b), c_(c),d_(d) {}
    int operator ()()
    {
        std::cout << a_ << " " << b_ << " " << c_ << " " << d_ << std::endl;
        return 1;
    }
private:
    int a_,d_=0;
    double b_ = 0.0;
    std::string c_ = "";
};

// 自行封装构造过程
template <typename T, typename... Args>
T Instance(Args &&...args) 
{
    return T(args...);
}

void TestOwner() 
{
    std::tuple t(3, 1.9, "test",8);
    Foo&& f = std::apply([](auto &&...args)->Foo {return Instance<Foo>(args...); }, std::move(t));
    f(); 

}

int main() 
{
    TestOwner();
    return 0;
}

反复提到过一句话,简单才是王道。为什么js和python流行,原因何在?所谓功能强大,其实都是在简单的基础上喊出来的。c++功能更强大,怎么没有他们流行?这就是原因,不简单。

四、总结

std::apply和std::make_from_tuple可以说是一对相互配合操作的工具接口,通过这两个就可以把一些较为复杂的对std::tuple的操作变得容易简单。看STL库中,还有一个forward_as_tuple函数,基本都差不多,有兴趣可以认真看看。
其实在实际工作也可以发现,一个好的接口往往比实现本身更容易为人所称道。本文的这两个函数,其实就可以理解为std::tuple基础设施,让其在实际应用中更方便快捷安全的使用的一个接口。这个现象在STL中其实很多,这也符合一个普通应用库的要求。不过,不是说这样的接口越多越好,还是尽量简化成较少的接口为妙。毕竟,学习的东西越多,反而会让开发者产生厌烦。过犹不及就是这个道理。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: make_tupleC++ STL库的一个函数,用于创建一个tuple(元组)对象,即一个固定长度的不可变的数据结构,其可以存放不同类型的数据。函数的语法为: ```c++ template <class... Types> tuple<Types...> make_tuple(Types&&... args); ``` 其Types为元组元素的类型,args为元组元素的值。make_tuple函数将args的值按顺序依次存入一个tuple对象,并返回该对象的副本。 ### 回答2: make_tuple是一个用于创建元组(tuple)的函数模板,它可以将多个值组合成一个元组对象。 在C++,元组是一种不同类型值的集合,这些值可以是同种类型或不同类型的。通过make_tuple函数,我们可以轻松地创建这样的元组对象。 make_tuple函数的使用非常简单,只需传入所要组合的值作为参数即可。它会自动根据参数类型推导出所创建元组的类型,并返回该元组。 例如,我们可以调用make_tuple函数创建一个包含整数、浮点数和字符串的元组对象,代码如下: ``` auto myTuple = make_tuple(10, 3.14, "hello"); ``` 上述代码将创建一个名为myTuple的元组,其包含整数10、浮点数3.14和字符串"hello"。make_tuple函数会根据传入的参数类型自动推导出元组类型,即`tuple<int, double, const char*>`。 创建元组后,我们可以使用std::get<I>(tuple)函数来访问元组指定位置的元素值。其,I表示元素在元组的位置,从0开始计数。 总结来说,make_tuple函数是一个方便的工具,可以用于创建包含不同类型值的元组对象。它可以大大简化元组的创建过程,提高代码的可读性和可维护性。 ### 回答3: make_tupleC++标准库的一个模板函数,用于创建一个元组(tuple)。元组是一种容器,可以存储多个不同类型的对象,并且元组的长度是固定的。使用make_tuple函数可以方便地创建一个元组对象。 make_tuple函数的语法如下: template <class... Types> tuple<VTypes...> make_tuple(Types&&... args); 其,Types是待存储到元组的对象的类型列表,args是传递给这些对象的实际参数。make_tuple函数会根据实际参数的类型自动推导出元组的对象类型,并创建一个对应的元组对象。 例如,可以使用make_tuple函数创建一个包含整数和字符串的元组: auto myTuple = make_tuple(123, "Hello"); 上述代码将创建一个元组myTuple,包含一个整数对象123和一个字符串对象"Hello"。此时,myTuple的类型将被推导为tuple<int, const char*>。 在实际使用,我们可以通过索引或使用get函数来访问元组的对象。例如,可以使用如下方式访问元组myTuple的第一个对象(索引从0开始): int myInt = get<0>(myTuple); 总结来说,make_tuple是一个创建元组对象的模板函数,通过传递给它的实际参数,可以自动推导出元组对象的类型,并创建一个对应的元组。元组在C++是一个能够存储不同类型对象的容器,可以通过索引或使用get函数来访问元组的对象。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值