python中大量使用函數包裝器。用C++也能用簡潔的方式實現這個功能。可以把這個包裝器實現成一個函數。
我們把實現包裝器功能的這個函數命名爲TimeMonitorFunc。下面先進行概述,然後介紹一下具體實現,最後介紹把這個包裝器修改成一個簡化的版本(注意,不是優化而是簡化,功能也減少了)。
概述:
(1)功能:
任何一個可調用對象(例如函數)都能和TimeMonitorFunc結合在一起使用。這個包裝器的功能是執行任意一個可調用對象並得出執行時間。
(2)原型:
template <typename T,typename... Args>
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;
}
int ToInt(string str)
{
this_thread::sleep_for(seconds(1));
return atoi(str.c_str());
}
template <typename T,typename... Args>
auto TimeMonitorFunc(long long& time, T&& func, Args&&... params)
{
long long start_timestamp = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
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;
string s = TimeMonitorFunc(run_duration, ToString,10, 20, 'a');
cout << s << " " << run_duration <<endl;
int number = TimeMonitorFunc(run_duration, ToInt,"555");
cout << number << " " << run_duration << endl;
getchar();
}
TimeMonitorFunc的參數列表和語句:auto&& ret = forward<decltype(func)>(func)(forward<decltype(params)>(params)...);涉及了多個C++11的名詞,簡單羅列這些名詞,比如模板參數包、完美轉發、萬能引用,這種名詞堆砌不友好。可以談一下這兩行代碼做了什麼。我的介紹不能完備的解釋這些技術,只是開了個頭。
參數列表中的T&& func和 Args&&... params相比普通的模板形參聲明多了一個&&的修飾,這用來把形參聲明成萬能引用形參,效果是如果傳入的實參是右值,那麼模板函數實例化後形參變量的類型是右值,反之是左值。
函數體內使用了forward<decltype(func)>(func)和 forward<decltype(params)>(params)...這種用法也和右值有關。即使函數的形參變量是右值類型,在函數中使用這些形參變量時,它還是左值,因爲對函數來說,這個形參變量就是一個局部變量;把函數形參變量聲明成右值類型只是向編譯器聲明這個函數只接受右值類型的實參。forward的作用是:如果形參變量聲明成右值類型,forward把這個變量轉換成右值類型。在函數體內使用這個變量時,就可以當作右值使用了。如果形參變量是左值,則不進行這個轉換。我可以透露,forward是根據<>內的信息判斷該不該轉換的。
簡化版本:
介紹了這麼多和右值有關的事情,而且遠遠沒有說透徹。右值似乎是個麻煩。捨棄這麼麻煩也是可以的,不支持右值。我希望最好還是支持右值。代碼如下:
template <typename T,typename... Args>
auto TimeMonitorFunc(long long& time, T& func, Args... params)
{
long long start_timestamp = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
auto ret = func(params...);
long long end_timestamp = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
time = end_timestamp - start_timestamp;
return ret;
}