考虑远程调用的情况下,我们通过一定的技术手段,通知远程服务器调用某个函数来执行相应的功能,这就会涉及到万能引用与完美转发,其常见形式如下:
// <1>定义一个需要被调用的函数
void func(int a, double b, const char &c)
{
std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;
}
// <2>定义一个转发函数模板,完美转发args参数的值类型和值类别
template<typename F, typename... Args>
void forwardFunc(F&& func, Args&&... args)
{
func(std::forward<Args>(args)...);
}
函数使用例子如下:
int main()
{
int value = 1;
forwardFunc(func, 1, 2.3, 'c');
return 0;
}
但是在远程调用的情况下,我们需要将参数通过网络发送到服务器,就无法像上面用例那样直接调用函数并传递参数。但是我们可以通过一定的技术手段,明确的知道当前调用的是哪一个函数,以及这个函数的参数个数,便可以想办法将报文内容反序列化为std::tuple对象,并对其进行函数调用(反序列化的内容见第4节,这里假定已经可以成功得到std::tuple对象)
// 具体实现函数模板:用tuple做参数调用函数模板类
template<typename Function, typename Tuple, std::size_t... Index>
decltype(auto) invoke_impl(Function&& func, Tuple&& t, std::index_sequence<Index...>)
{
return func(std::get<Index>(std::forward<Tuple>(t))...);
}
// 函数模板:作为一个接口,负责调用相关函数并传递参数
template<typename Function, typename Tuple>
decltype(auto) invoke(Function&& func, Tuple&& t)
{
constexpr auto size = std::tuple_size<typename std::decay<Tuple>::type>::value;
return invoke_impl(std::forward<Function>(func), std::forward<Tuple>(t), std::make_index_sequence<size>{});
}
温馨提示:关于万能引用和完美转发的用法
template<typename F, typename... Args>
void forwardFunc(F&& func, Args&&... args)
{
func(std::forward<Args>(args)...);
}
以这里的F&& func
举例来说,使用&&
表示万能引用,而非右值引用,是为了解决F func
丢失变量的cv属性(const和volatile)。
使用std::forward<Args>(args)...
进行完美转发是为了解决值类别(左值或右值)问题,若不采用std::forward<Args>(args)...
进行转发,即使Args&&... args
作为forwardFunc(F&& func, Args&&... args)
的参数被模板推导为右值引用类型,也会在forwardFunc(F&& func, Args&&... args)
函数内变为左值(换句话说,就算args的类型被推导为右值引用类型,但args本身为左值,因为args仍可以在forwardFunc(F&& func, Args&&... args)
内被重新赋值),而完美转发可以将其恢复为原本的值类别(原本是左值,完美转发后还是左值;原本是右值,完美转发后还是右值)。