编译器如何生成模板类定义

How does compiler generate template class definitions?

本文关键字:定义 何生成 编译器      更新时间:2023-10-16

考虑以下头文件。

在我的Object.h

#ifndef OBJECT_H_
#define OBJECT_H_
#include <iostream>
template <class Policy>
class Object
{
  friend Policy;
 private:
  Policy* p;
  int hash;
 public:
 Object():p(new Policy(this)),hash(0){}
  ~Object(){delete p;}
  void set(){p->setHash();}
  void show() const{std::cout<<"hash = "<<hash<<std::endl;}
};
#endif // OBJECT_H_

然后,我创建了两个具有不同哈希值的策略P1和P2。

p1.h

#ifndef P1_H_
#define P1_H_
template <class Policy> class Object;
class P1
{
  enum {p1 = 1}; 
  Object<P1>* t;
 public:
  P1(Object<P1>* at):t(at){}
  void setHash(){t->hash = p1;}
};
#endif // P1_H_

p2.h

#ifndef P2_H_
#define P2_H_
template <class Policy> class Object;
class P2
{
 enum {p2 = 2};
 Object<P2>* t;
 public:
 P2(Object<P2>* at):t(at){}
 //void setHash(){t->hash = p2;} // ** Notice here **
};
#endif // P2_H_

在我的main.cpp中,我有

#include "Object.h"
#include "p1.h"
#include "p2.h"
int main()
{
  Object<P1> t1;
  t1.set();
  t1.show();
  Object<P2> t2;
  //t2.set();
  t2.show();
}

操作系统X上的结果

hash=1

hash=0

观察

  1. 它将正确编译和运行。请注意,Object<P2>的定义甚至还不完整。由于P2尚未定义setHash()。

  2. 如果我删除了main中的注释,那么它将报告一个错误。

    /Object.h:20:8:错误:"P2"中没有名为"setHash"的成员

    main.cpp:12:6:注意:在实例化成员函数"Object::set"时,此处请求t2.set();

问题

  1. 考虑一下P1或P2类中的Object成员,即使我没有在main中创建Object<P1>Object<P2的实例,编译器也会为它们中的每一个生成类定义吗?

  2. 为什么Object<P2>可以?由于定义不完整。

考虑一下P1或P2类中的Object成员,即使我没有在main中创建Object<P1>Object<P2>的实例,编译器也会为它们中的每一个生成类定义吗?

没有。成员是Object<P1>* t;Object<P1>* t;——它们不是static,所以只有在创建P1P2对象实例时才会创建,而且它们是指针,所以即使在那时也不会创建Object<P1>*:传递给构造函数的指针(可能是nullptr/0)也会被简单地存储。

为什么对象可以?由于定义不完整。

对于模板,函数只有在被调用时才会被实例化。只要它们能够被正确地解析,这就不是错误。

P1P2中没有Object<P1>Object<P2>成员。共有Object<P1>*Object<P2>*两个成员。指向不完整类型的指针完全可以。除非取消引用指针,否则类型本身不会被实例化。

main函数中,Object<P2>是一个完整的类型,但它的非虚拟成员函数仍然不会实例化,除非调用(或者更确切地说,从技术上讲,使用ODR)。这就是为什么有Object<P2> t2;是可以的,但调用t2.set()是一个错误。