C++基础专栏:http://t.csdnimg.cn/sXo1L
相关系列文章
目录
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);
}
...