【C++ 小问答】1:模板实例化

Table Of Contents

以下 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

模板是根据入参的类型来进行实例化的。此处需要注意两点:

  1. f1()f2() 有不同的函数签名(function signature),但在作为参数传给函数时两者的类型是一样的,皆为 void (*)(bool)。因此 foo 模板只会为他们俩实例化一次。
  2. 对第 16、17 行的两处 foo([](bool x){})foo 模板看似只会实例化一次,实则不然。C++ 中 Lambda 的实现方式是使用匿名结构体(anonymous struct)1,而两个不同名的结构体即使定义完全相同,也是两个不同的结构体,即不同的类型。因此 foo 模板会为两者实例化两次。

  1. 此处“匿名”是指对用户(即开发者)匿名,编译器还是会赋予它们内部命名以便编译器作区分。 ↩︎


一个简洁实用的进入 Chroot 环境的脚本
【C++ 小问答】2:平台对隐式类型转换的影响