包装器这种编程方法是内建到python的语言里的。C++也能很容易实现这个功能。
本文的包装器能为任意一个可调用对象附加一个输出执行时间的功能。我们把这个包装器实现成函数TimeMonitorFunc。下面先进行概述,然后介绍一下具体实现。
概述:
(1)功能:
任何一个可调用对象(例如函数)都能和TimeMonitorFunc结合在一起使用。这个包装器的功能是执行任意一个可调用对象并得出执行时间。
(2)原型:
template <typename T,typename... Args>
decltype(auto) TimeMonitorFunc(long long& time, T&& func, Args&&... params)
(3)描述:
第一个参数time是一个传出参数,表示函数执行了多少毫秒。把可调用对象传给形参func,把可调用对象的参数在func后依次传入,TimeMonitorFunc的返回值即func的返回值。
下面介绍具体实现:
#include "pch.h"
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
using namespace std;
using namespace std::chrono;
std::string ToString(int a, int b, char c)
{
this_thread::sleep_for(seconds(1));
return to_string(a) + to_string(b) + c;
}
template <typename T,typename... Args>
decltype(auto) TimeMonitorFunc(long long& time, T&& func, Args&&... params)
{
long long start_timestamp = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
decltype(auto) ret = forward<decltype(func)>(func)(forward<decltype(params)>(params)...);
long long end_timestamp = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
time = end_timestamp - start_timestamp;
return ret;
}
int main()
{
long long run_duration;
int n = 10;
string str = TimeMonitorFunc(run_duration, ToString,n, 20, 'a');
cout << str << " " << run_duration <<endl;
getchar();
}
auto作为返回值是C++14特性,decltype(auto) 的作用和auto类似,也是表示返回值的类型通过推到获得,但类型的推导规则和auto略有不同,本文暂不讨论推导规则。
Args&& 、T&& func 、forward<>结合起来实现参数的完美转发。这个包装器的本质就是TimeMonitorFunc嵌套了一个func,func的参数是通过TimeMonitorFunc的参数(params)获得的。我们希望传递给func的实参是传递给TimeMonitorFunc的实参本身,而不是它的副本(按值传递),并且实参的const、volation、左值、右值等属性不变。
Args&&和T&& fun声明的是万能引用类型的模板形参。这两个模板形参实例化为普通形参时,在形参类型中保留了实参的const、volation、左值、右值等属性,并且形参被实例化为引用类型(左值引用或右值引用),而非值类型。“decltype(auto) TimeMonitorFunc(run_duration, ToString,n, 20, 'a')”实例化的结果是:“decltype(auto) TimeMonitorFunc(__int64& time,string(__cdecl &)(int,int,char),int& n1,int&& n2,char&& c)”。形参变量n2和c虽然声明为右值引用类型,但是这两个变量在作为实参传递给func时(形如:func(n1,n2,c)),对func来说n2、c是左值。存在这样一个事实,右值引用类型的变量本身是左值。forward的作用是把右值引用类型的形参变量转换为右值,左值引用类型的形参变量保持左值不变。最终TimeMonitorFunc原原本本的把参数转发给了func。