首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

std::apply源码分析

  • 24-03-12 16:27
  • 3956
  • 7062
blog.csdn.net

介绍

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

std::apply 的语法如下:

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

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

源码分析

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

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

  1. template <typename _Fn, typename _Tuple>
  2. constexpr decltype(auto)
  3. apply(_Fn&& __f, _Tuple&& __t)
  4. noexcept(__unpack_std_tuple)
  5. {
  6. using _Indices
  7. = make_index_sequenceremove_reference_t<_Tuple>>>;
  8. return std::__apply_impl(std::forward<_Fn>(__f),
  9. std::forward<_Tuple>(__t),
  10. _Indices{});
  11. }

    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实现

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

看到这里是不是很熟悉了,也是调用了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实现

  1. template<typename _Callable, typename... _Args>
  2. constexpr typename __invoke_result<_Callable, _Args...>::type
  3. __invoke(_Callable&& __fn, _Args&&... __args)
  4. noexcept(__is_nothrow_invocable<_Callable, _Args...>::value)
  5. {
  6. using __result = __invoke_result<_Callable, _Args...>;
  7. using __type = typename __result::type;
  8. using __tag = typename __result::__invoke_type;
  9. return std::__invoke_impl<__type>(__tag{}, std::forward<_Callable>(__fn),
  10. std::forward<_Args>(__args)...);
  11. }

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

  1. struct __invoke_memfun_ref { };
  2. struct __invoke_memfun_deref { };
  3. struct __invoke_memobj_ref { };
  4. struct __invoke_memobj_deref { };
  5. struct __invoke_other { };

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

4、__invoke_result实现

  1. template<typename _Functor, typename... _ArgTypes>
  2. struct __invoke_result
  3. : public __result_of_impl<
  4. is_member_object_pointer<
  5. typename remove_reference<_Functor>::type
  6. >::value,
  7. is_member_function_pointer<
  8. typename remove_reference<_Functor>::type
  9. >::value,
  10. _Functor, _ArgTypes...
  11. >::type
  12. { };

5、std::__invoke_impl实现

  1. //左值转化为右值
  2. template<typename _Tp, typename _Up = typename __inv_unwrap<_Tp>::type>
  3. constexpr _Up&&
  4. __invfwd(typename remove_reference<_Tp>::type& __t) noexcept
  5. { return static_cast<_Up&&>(__t); }
  6. //全局函数,类static函数,lambda函数,仿函数等
  7. template<typename _Res, typename _Fn, typename... _Args>
  8. constexpr _Res
  9. __invoke_impl(__invoke_other, _Fn&& __f, _Args&&... __args)
  10. { return std::forward<_Fn>(__f)(std::forward<_Args>(__args)...); }
  11. //类的成员函数
  12. template<typename _Res, typename _MemFun, typename _Tp, typename... _Args>
  13. constexpr _Res
  14. __invoke_impl(__invoke_memfun_ref, _MemFun&& __f, _Tp&& __t,
  15. _Args&&... __args)
  16. { return (__invfwd<_Tp>(__t).*__f)(std::forward<_Args>(__args)...); }
  17. //类的成员函数
  18. template<typename _Res, typename _MemFun, typename _Tp, typename... _Args>
  19. constexpr _Res
  20. __invoke_impl(__invoke_memfun_deref, _MemFun&& __f, _Tp&& __t,
  21. _Args&&... __args)
  22. {
  23. return ((*std::forward<_Tp>(__t)).*__f)(std::forward<_Args>(__args)...);
  24. }
  25. //类的成员变量,用std::invoke也可以访问类成员数据
  26. template<typename _Res, typename _MemPtr, typename _Tp>
  27. constexpr _Res
  28. __invoke_impl(__invoke_memobj_ref, _MemPtr&& __f, _Tp&& __t)
  29. { return __invfwd<_Tp>(__t).*__f; }
  30. //类的成员变量,用std::invoke也可以访问类成员数据
  31. template<typename _Res, typename _MemPtr, typename _Tp>
  32. constexpr _Res
  33. __invoke_impl(__invoke_memobj_deref, _MemPtr&& __f, _Tp&& __t)
  34. { return (*std::forward<_Tp>(__t)).*__f; }

示例

示例1:

  1. #include
  2. #include
  3. #include
  4. int add(int first, int second) { return first + second; }
  5. template<typename T>
  6. T add_generic(T first, T second) { return first + second; }
  7. auto add_lambda = [](auto first, auto second) { return first + second; };
  8. template<typename... Ts>
  9. std::ostream& operator<<(std::ostream& os, std::tuple const& theTuple)
  10. {
  11. std::apply
  12. (
  13. [&os](Ts const&... tupleArgs)
  14. {
  15. os << '[';
  16. std::size_t n{0};
  17. ((os << tupleArgs << (++n != sizeof...(Ts) ? ", " : "")), ...);
  18. os << ']';
  19. }, theTuple
  20. );
  21. return os;
  22. }
  23. int main()
  24. {
  25. // OK
  26. std::cout << std::apply(add, std::pair(1, 2)) << '\n';
  27. // 错误:无法推导函数类型
  28. // std::cout << std::apply(add_generic, std::make_pair(2.0f, 3.0f)) << '\n';
  29. // OK
  30. std::cout << std::apply(add_lambda, std::pair(2.0f, 3.0f)) << '\n';
  31. // 进阶示例
  32. std::tuple myTuple(25, "Hello", 9.31f, 'c');
  33. std::cout << myTuple << '\n';
  34. }

输出:

3
5
[25, Hello, 9.31, c]

示例2:

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

  1. using DataTransType = std::tuple<int, const char*, quint64>;
  2. void CObjectManager::notify(DataTransType& Param)
  3. {
  4. return std::apply(std::bind(&CObjectManager::_notify, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), Param);
  5. }
  6. void CObjectManager::_notify(int type, const char* data, quint64 size)
  7. {
  8. m_pDataCmdPro->dispatchData(data, size, type);
  9. }
  10. ...

参考

std::apply - cppreference.com

介绍

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

std::apply 的语法如下:

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

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

源码分析

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

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

  1. template <typename _Fn, typename _Tuple>
  2. constexpr decltype(auto)
  3. apply(_Fn&& __f, _Tuple&& __t)
  4. noexcept(__unpack_std_tuple)
  5. {
  6. using _Indices
  7. = make_index_sequenceremove_reference_t<_Tuple>>>;
  8. return std::__apply_impl(std::forward<_Fn>(__f),
  9. std::forward<_Tuple>(__t),
  10. _Indices{});
  11. }

    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实现

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

看到这里是不是很熟悉了,也是调用了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实现

  1. template<typename _Callable, typename... _Args>
  2. constexpr typename __invoke_result<_Callable, _Args...>::type
  3. __invoke(_Callable&& __fn, _Args&&... __args)
  4. noexcept(__is_nothrow_invocable<_Callable, _Args...>::value)
  5. {
  6. using __result = __invoke_result<_Callable, _Args...>;
  7. using __type = typename __result::type;
  8. using __tag = typename __result::__invoke_type;
  9. return std::__invoke_impl<__type>(__tag{}, std::forward<_Callable>(__fn),
  10. std::forward<_Args>(__args)...);
  11. }

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

  1. struct __invoke_memfun_ref { };
  2. struct __invoke_memfun_deref { };
  3. struct __invoke_memobj_ref { };
  4. struct __invoke_memobj_deref { };
  5. struct __invoke_other { };

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

4、__invoke_result实现

  1. template<typename _Functor, typename... _ArgTypes>
  2. struct __invoke_result
  3. : public __result_of_impl<
  4. is_member_object_pointer<
  5. typename remove_reference<_Functor>::type
  6. >::value,
  7. is_member_function_pointer<
  8. typename remove_reference<_Functor>::type
  9. >::value,
  10. _Functor, _ArgTypes...
  11. >::type
  12. { };

5、std::__invoke_impl实现

  1. //左值转化为右值
  2. template<typename _Tp, typename _Up = typename __inv_unwrap<_Tp>::type>
  3. constexpr _Up&&
  4. __invfwd(typename remove_reference<_Tp>::type& __t) noexcept
  5. { return static_cast<_Up&&>(__t); }
  6. //全局函数,类static函数,lambda函数,仿函数等
  7. template<typename _Res, typename _Fn, typename... _Args>
  8. constexpr _Res
  9. __invoke_impl(__invoke_other, _Fn&& __f, _Args&&... __args)
  10. { return std::forward<_Fn>(__f)(std::forward<_Args>(__args)...); }
  11. //类的成员函数
  12. template<typename _Res, typename _MemFun, typename _Tp, typename... _Args>
  13. constexpr _Res
  14. __invoke_impl(__invoke_memfun_ref, _MemFun&& __f, _Tp&& __t,
  15. _Args&&... __args)
  16. { return (__invfwd<_Tp>(__t).*__f)(std::forward<_Args>(__args)...); }
  17. //类的成员函数
  18. template<typename _Res, typename _MemFun, typename _Tp, typename... _Args>
  19. constexpr _Res
  20. __invoke_impl(__invoke_memfun_deref, _MemFun&& __f, _Tp&& __t,
  21. _Args&&... __args)
  22. {
  23. return ((*std::forward<_Tp>(__t)).*__f)(std::forward<_Args>(__args)...);
  24. }
  25. //类的成员变量,用std::invoke也可以访问类成员数据
  26. template<typename _Res, typename _MemPtr, typename _Tp>
  27. constexpr _Res
  28. __invoke_impl(__invoke_memobj_ref, _MemPtr&& __f, _Tp&& __t)
  29. { return __invfwd<_Tp>(__t).*__f; }
  30. //类的成员变量,用std::invoke也可以访问类成员数据
  31. template<typename _Res, typename _MemPtr, typename _Tp>
  32. constexpr _Res
  33. __invoke_impl(__invoke_memobj_deref, _MemPtr&& __f, _Tp&& __t)
  34. { return (*std::forward<_Tp>(__t)).*__f; }

示例

示例1:

  1. #include
  2. #include
  3. #include
  4. int add(int first, int second) { return first + second; }
  5. template<typename T>
  6. T add_generic(T first, T second) { return first + second; }
  7. auto add_lambda = [](auto first, auto second) { return first + second; };
  8. template<typename... Ts>
  9. std::ostream& operator<<(std::ostream& os, std::tuple const& theTuple)
  10. {
  11. std::apply
  12. (
  13. [&os](Ts const&... tupleArgs)
  14. {
  15. os << '[';
  16. std::size_t n{0};
  17. ((os << tupleArgs << (++n != sizeof...(Ts) ? ", " : "")), ...);
  18. os << ']';
  19. }, theTuple
  20. );
  21. return os;
  22. }
  23. int main()
  24. {
  25. // OK
  26. std::cout << std::apply(add, std::pair(1, 2)) << '\n';
  27. // 错误:无法推导函数类型
  28. // std::cout << std::apply(add_generic, std::make_pair(2.0f, 3.0f)) << '\n';
  29. // OK
  30. std::cout << std::apply(add_lambda, std::pair(2.0f, 3.0f)) << '\n';
  31. // 进阶示例
  32. std::tuple myTuple(25, "Hello", 9.31f, 'c');
  33. std::cout << myTuple << '\n';
  34. }

输出:

3
5
[25, Hello, 9.31, c]

示例2:

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

  1. using DataTransType = std::tuple<int, const char*, quint64>;
  2. void CObjectManager::notify(DataTransType& Param)
  3. {
  4. return std::apply(std::bind(&CObjectManager::_notify, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), Param);
  5. }
  6. void CObjectManager::_notify(int type, const char* data, quint64 size)
  7. {
  8. m_pDataCmdPro->dispatchData(data, size, type);
  9. }
  10. ...

参考

std::apply - cppreference.com

注:本文转载自blog.csdn.net的流星雨爱编程的文章"https://blog.csdn.net/haokan123456789/article/details/134853662"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top