本文為C++ Weekly Ep 333的note
首先先來看code: - Compiler Explorer
這次的目標是要土炮一個精神上與std::function差不多的function
class,就如同
OK,讓我們一步一步來看這段code
Primary template
首先我們必須先定義Primary template,後面會對function做partial
template specialization 1
2template <typename T>
class function;
class function
再來是我們的function物件
1
2
3
4
5
6
7
8
9
10
11
12
13template <typename Ret, typename ... Param>
class function<Ret (Param...)>
{
public:
template<typename FunctionObject>
function(FunctionObject fo)
: callable(std::make_unique<callable_impl<FunctionObject>(std::move(fo))) {}
Ret operator()(Param... param) {return callable->call(param...);}
private:
/*下面再講,這裡先不看*/
std::unique_ptr<callable_interface> callable ;
};
我們可以看到Ret (Param...)會被展開成int (int, int),我們寫在main裏頭的:
1
2function<int (int, int)> func{f};
// ^heretypename FunctionObject會被換成你傳進去的callable object的type(),然後用你給的fo去初始化那個unique_ptr(為什麼要用unique_ptr等等會講)
callable_interface & callable_impl
然後整個class function最精華的地方來了,我們先定義一個pure virtual(又稱interface) callable_interface作為我們的base
1
2
3
4
5
6
7
8
9
10
11
12struct callable_interface {
virtual Ret call(Param...) = 0;
virtual ~callable_interface() = default;
};
template <typename Callable>
struct callable_impl : callable_interface {
callable_impl(Callable callable_) : callable(std::move(callable_)){}
Ret call(Param... param) { return std::invoke(callable, param...);}
Callable callable;
};接下來讓callable_impl去繼承callable_interface,使其變成callable_interface的derived class,現在讓我們注意callable_impl的call(),讓我們來看一下cpp_insight的輸出
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
42template<typename Callable>
struct callable_impl;
/* First instantiated from: type_traits:1392 */
template<>
struct callable_impl<int (*)(int, int)> : public callable_interface
{
inline callable_impl(int (*callable_)(int, int))
: callable_interface()
, callable{std::move(callable_)}
{
}
inline virtual int call(int __param0, int __param1)
{
return std::invoke(this->callable, __param0, __param1);
}
int (*callable)(int, int);
// inline virtual constexpr ~callable_impl() noexcept = default;
};
/* First instantiated from: type_traits:1392 */
template<>
struct callable_impl<__lambda_39_36> : public callable_interface
{
inline callable_impl(__lambda_39_36 callable_)
: callable_interface()
, callable{__lambda_39_36(std::move(callable_))}
{
}
inline virtual int call(int __param0, int __param1)
{
return std::invoke(this->callable, __param0, __param1);
}
__lambda_39_36 callable;
// inline virtual constexpr ~callable_impl() noexcept = default;
};
我們可以發現:
- struct callable_impl<int (*)(int, int)>
- struct callable_impl<__lambda_39_36>
編譯器幫我們生出了一個raw function
pointer,一個lambda的callable_impl,又因為call()是一個virtual,然後我們還使用了unique_ptr
這就代表無論我的FunctionObject最後是什麼,只要他能夠被call,callable_impl裏頭的call()一定就能夠透過std::invoke()產生出正確的呼叫程式碼
至此整個function object的原理就說明完了
結語
- 你各位要hold住阿