问
以下 foo 函数模板被实例化多少次?
1template <typename T>
2void foo(T f) {}
3
4struct Bar {
5 void operator()(bool x) {}
6};
7
8void f1(bool x) {}
9
10void f2(bool x) {}
11
12int main() {
13 foo(f1);
14 foo(Bar());
15 foo(f2);
16 foo([](bool x){});
17 foo([](bool x){});
18}
答
使用 C++ Insights 生成该代码的编译器视角:
1template <typename T>
2void foo(T f) {}
3
4/* First instantiated from: insights.cpp:13 */
5#ifdef INSIGHTS_USE_TEMPLATE
6template<>
7void foo<void (*)(bool)>(void (*f)(bool)) {}
8#endif
9
10/* First instantiated from: insights.cpp:14 */
11#ifdef INSIGHTS_USE_TEMPLATE
12template<>
13void foo<Bar>(Bar f) {}
14#endif
15
16/* First instantiated from: insights.cpp:16 */
17#ifdef INSIGHTS_USE_TEMPLATE
18template<>
19void foo<__lambda_16_7>(__lambda_16_7 f) {}
20#endif
21
22/* First instantiated from: insights.cpp:17 */
23#ifdef INSIGHTS_USE_TEMPLATE
24template<>
25void foo<__lambda_17_7>(__lambda_17_7 f) {}
26#endif
27
28struct Bar {
29 inline void operator()(bool x) {}
30};
31
32void f1(bool x) {}
33
34void f2(bool x) {}
35
36int main() {
37 foo(f1);
38 foo(Bar());
39 foo(f2);
40
41 class __lambda_16_7 {
42 public:
43 inline /*constexpr */ void operator()(bool x) const {}
44
45 using retType_16_7 = void (*)(bool);
46 inline /*constexpr */ operator retType_16_7 () const noexcept
47 {
48 return __invoke;
49 };
50
51 private:
52 static inline void __invoke(bool x) {}
53 };
54
55 foo(__lambda_16_7{});
56
57 class __lambda_17_7 {
58 public:
59 inline /*constexpr */ void operator()(bool x) const {}
60
61 using retType_17_7 = void (*)(bool);
62 inline /*constexpr */ operator retType_17_7 () const noexcept
63 {
64 return __invoke;
65 };
66
67 private:
68 static inline void __invoke(bool x) {}
69 };
70
71 foo(__lambda_17_7{});
72}
从注释可以看出, foo 函数模板被实例化了 4 次,分别对应以下 4 处:
1foo(f1); // Line 13
2foo(Bar()); // Line 14
3foo(f2);
4foo([](bool x){}); // Line 16
5foo([](bool x){}); // Line 17
模板是根据入参的类型来进行实例化的。此处需要注意两点:
f1()
和f2()
有不同的函数签名(function signature),但在作为参数传给函数时两者的类型是一样的,皆为void (*)(bool)
。因此foo
模板只会为他们俩实例化一次。- 对第 16、17 行的两处
foo([](bool x){})
,foo
模板看似只会实例化一次,实则不然。C++ 中 Lambda 的实现方式是使用匿名结构体(anonymous struct)1,而两个不同名的结构体即使定义完全相同,也是两个不同的结构体,即不同的类型。因此foo
模板会为两者实例化两次。
-
此处“匿名”是指对用户(即开发者)匿名,编译器还是会赋予它们内部命名以便编译器作区分。 ↩︎