std::apply使用及源码分析

C++基础专栏:http://t.csdnimg.cn/sXo1L 

相关系列文章

C++17之折叠表达式

C++之std::tuple(一) : 使用精讲(全)

C++之std::tuple(二) : 揭秘底层实现原理

目录

1.介绍

2.源码分析

3.示例

4.参考


1.介绍

std::apply 是 C++17 中引入的一个函数模板,位于 <tuple> 头文件中。它允许以元组的方式将参数传递给一个可调用对象(函数、函数指针、成员函数指针、函数对象等)。

std::apply 的语法如下:

template< class F, class Tuple >                       (C++17 起)
constexpr decltype(auto) apply( F&& f, Tuple&& t );    (C++23 前)
template< class F, tuple-like Tuple >
constexpr decltype(auto) apply( F&& f, Tuple&& t ) noexcept(/* 见下文 */);  (C++23 起)

 以元组 t 的元素作为参数调用可调用对象 f

2.源码分析

        本文是以.\Qt6.5.2\Tools\mingw1120_64\lib\gcc\x86_64-w64-mingw32\11.2.0\include\c++目录下的tuple文件为蓝本进行分析的。

        1、std::apply的实现代码:

    template <typename _Fn, typename _Tuple>
    constexpr decltype(auto)
    apply(_Fn&& __f, _Tuple&& __t)
    noexcept(__unpack_std_tuple<is_nothrow_invocable, _Fn, _Tuple>)
    {
      using _Indices
	= make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>;
      return std::__apply_impl(std::forward<_Fn>(__f),
			       std::forward<_Tuple>(__t),
			       _Indices{});
    }

    tuple_size_v : 是计算__t的大小
    make_index_sequence :产生一个序列,假如tupe_size_v计算出结果是10,则产生的序列为

                                              {0,1,2,3,4,5,6,7,8,9}
    std::__apply_impl :  std::apply的详细实现

2、std::__apply_impl实现

    template <typename _Fn, typename _Tuple, size_t... _Idx>
    constexpr decltype(auto)
    __apply_impl(_Fn&& __f, _Tuple&& __t, index_sequence<_Idx...>)
    {
      return std::__invoke(std::forward<_Fn>(__f),
			   std::get<_Idx>(std::forward<_Tuple>(__t))...);
    }

看到这里是不是很熟悉了,也是调用了std::__invoke, __f为可调用对象,用std::forward完美转发了__f,  std::get<_Idx>(std::forward<_Tuple>(__t))... 根据c++17引入的折叠表达式的右折叠规则,展开为std::get<0>(std::forward<_Tuple>(__t)), std::get<1>(std::forward<_Tuple>(__t)),std::get<2>(std::forward<_Tuple>(__t)), ..., std::get<9>(std::forward<_Tuple>(__t))

3、std::__invoke实现

    template<typename _Callable, typename... _Args>
    constexpr typename __invoke_result<_Callable, _Args...>::type
    __invoke(_Callable&& __fn, _Args&&... __args)
    noexcept(__is_nothrow_invocable<_Callable, _Args...>::value)
    {
      using __result = __invoke_result<_Callable, _Args...>;
      using __type = typename __result::type;
      using __tag = typename __result::__invoke_type;
      return std::__invoke_impl<__type>(__tag{}, std::forward<_Callable>(__fn),
					std::forward<_Args>(__args)...);
    }

 里面的__tag是根据__fn决策出可调用对象是指向非静态成员对象的指针或指向非静态成员函数的指针其它可调用对象,__tag的具体类型有:

  struct __invoke_memfun_ref { };
  struct __invoke_memfun_deref { };
  struct __invoke_memobj_ref { };
  struct __invoke_memobj_deref { };
  struct __invoke_other { };

std::__invoke_impl根据__tag的不同进行分派到具体的不同实现, 这叫 标签派发

4、__invoke_result实现

template<typename _Functor, typename... _ArgTypes>
    struct __invoke_result
    : public __result_of_impl<
        is_member_object_pointer<
          typename remove_reference<_Functor>::type
        >::value,
        is_member_function_pointer<
          typename remove_reference<_Functor>::type
        >::value,
	_Functor, _ArgTypes...
      >::type
    { };

5、std::__invoke_impl实现

    //左值转化为右值
    template<typename _Tp, typename _Up = typename __inv_unwrap<_Tp>::type>
    constexpr _Up&&
    __invfwd(typename remove_reference<_Tp>::type& __t) noexcept
    { return static_cast<_Up&&>(__t); }

   //全局函数,类static函数,lambda函数,仿函数等
  template<typename _Res, typename _Fn, typename... _Args>
    constexpr _Res
    __invoke_impl(__invoke_other, _Fn&& __f, _Args&&... __args)
    { return std::forward<_Fn>(__f)(std::forward<_Args>(__args)...); }

   //类的成员函数
  template<typename _Res, typename _MemFun, typename _Tp, typename... _Args>
    constexpr _Res
    __invoke_impl(__invoke_memfun_ref, _MemFun&& __f, _Tp&& __t,
		  _Args&&... __args)
    { return (__invfwd<_Tp>(__t).*__f)(std::forward<_Args>(__args)...); }

   //类的成员函数
  template<typename _Res, typename _MemFun, typename _Tp, typename... _Args>
    constexpr _Res
    __invoke_impl(__invoke_memfun_deref, _MemFun&& __f, _Tp&& __t,
		  _Args&&... __args)
    {
      return ((*std::forward<_Tp>(__t)).*__f)(std::forward<_Args>(__args)...);
    }

    //类的成员变量,用std::invoke也可以访问类成员数据
  template<typename _Res, typename _MemPtr, typename _Tp>
    constexpr _Res
    __invoke_impl(__invoke_memobj_ref, _MemPtr&& __f, _Tp&& __t)
    { return __invfwd<_Tp>(__t).*__f; }

    //类的成员变量,用std::invoke也可以访问类成员数据
  template<typename _Res, typename _MemPtr, typename _Tp>
    constexpr _Res
    __invoke_impl(__invoke_memobj_deref, _MemPtr&& __f, _Tp&& __t)
    { return (*std::forward<_Tp>(__t)).*__f; }

3.示例

示例1:

#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]

示例2:

std::apply也绑定类的成员函数, 大致代码如下:

using DataTransType = std::tuple<int, const char*, quint64>;

void CObjectManager::notify(DataTransType& Param)
{
    return std::apply(std::bind(&CObjectManager::_notify, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), Param);
}

void CObjectManager::_notify(int type, const char* data, quint64 size)
{
    m_pDataCmdPro->dispatchData(data, size, type);
}

...

4.参考

std::apply - cppreference.com

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值