前言 C++中想实现一个callable的对象,通常有四种方式:
std::function
:最common的方式,一般会配合std::bind
使用。
function pointer:最C的方式,但没办法实现有状态的callable object。
function object:就是重载了operator()
的类,C++98的STL中经常用。
lambda expression:不会污染namespace,一般来说编译器内部会实现为一个匿名的function object。
从原理上性能最好的应该是3和4,其次是2,最差的是std::function
(戳这里 )。下面我们用一小段代码来测试它们的性能。
测试结果
测试机器:MBP。
编译器:Apple LLVM version 8.1.0 (clang-802.0.42)。
编译方式:g++ test.cpp -std=c++14 -O2。
1 2 3 4 ./a.out "std::function" 0.15s user 0.20s system 98% cpu 0.358 total ./a.out "function_pointer" 0.10s user 0.11s system 98% cpu 0.209 total ./a.out "function_object" 0.03s user 0.01s system 92% cpu 0.042 total ./a.out "lambda" 0.03s user 0.01s system 93% cpu 0.042 total
可以看到3和4只要42ms,而相对应的2需要209ms,1需要358ms。这个顺序符合我们的预期,但相差这么多还是比较意外的。
测试程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include <iostream> #include <functional> #include <vector> #include <string> #include <utility> using namespace std;template <typename HandlerT = std::function<void (int )>>class Worker{public : explicit Worker (const HandlerT& handler): mHandler (handler) {} void Run (int x) { mHandler (x); } private : HandlerT mHandler; }; template <typename HandlerT>void Test (HandlerT&& h) { using WorkerT = Worker<HandlerT>; vector<WorkerT> v; for (int i = 0 ; i < 10000000 ; ++i) { v.emplace_back (std::forward<HandlerT>(h)); } int j = 0 ; for (auto & w: v) { w.Run (++j); } } void Func (int x) { int y = x + 5 ; y += 3 ; } struct Functor { void operator () (int x) const { int y = x + 5 ; y += 3 ; } }; int main (int argc, char ** argv) { if (argc != 2 ) { cerr << "error input" << endl; exit (1 ); } string mode{argv[1 ]}; if (mode == "std::function" ) { Test (bind (Func, placeholders::_1)); } else if (mode == "function_pointer" ) { Test (Func); } else if (mode == "function_object" ) { Test (Functor{}); } else if (mode == "lambda" ) { Test ([](int x) -> void {int y = x + 5 ; y += 3 ;}); } else { cerr << "error mode:" << mode << endl; exit (1 ); } }