Skip to the content.

隐式实例化

namespace jc {

template <typename T>
struct A;

A<int>* p = 0;  // OK:不需要类模板定义

template <typename T>
struct A {
  void f();
};

void g(A<int>& a) {  // 只使用类模板声明
  a.f();             // 使用了类模板定义,需要 A::f() 的定义
}

template <typename T>
void A<T>::f() {}

}  // namespace jc

int main() {}
namespace jc {

template <typename T>
struct A {
	A(int);
};

void f(A<double>) {}
void f(int) {}

}  // namespace jc

int main() {
  jc::f(42);  // 两个函数声明都匹配,调用第二个,但仍会实例化第一个
}

延迟实例化(Lazy Instantiation)

namespace jc {

template <int N>
struct A {
  int a[N];  // 编译器会假设 N 是正整数,实例化时 N <= 0 则失败
};

template <typename T, int N>
struct B {
  void f() {
    A<N> a;  // 如果 N <= 0,调用时出错
  }

  //   void error() {  // 即使不被调用也会引发错误
  //     A<-1> a;  // 要求给出 A<-1> 的完整定义,定义 -1 大小的数组出错
  //   }

  //   virtual void g();  // 虚函数只有声明没有定义会导致链接错误

  struct Nested {  // N <= 0 时使用该定义出错
    A<N> a;
  };

  //   union {    // union 的所有成员声明都会被生成
  //     A<N> a;  // N <= 0 时出错
  //   };
};

}  // namespace jc

int main() {
  jc::B<int, -1> b;
  //   b.f();                     // 调用则出错
  //   jc::B<int, -1>::Nested{};  // 错误
}

两阶段查找(Two-Phase Lookup)

POI(Points of Instantiation)

namespace jc {

struct A {
  A(int i) : i(i) {}
  int i;
};

A operator-(const A& a) { return A{-a.i}; }

bool operator<(const A& lhs, const A& rhs) { return lhs.i < rhs.i; }

using Int = A;  // 若使用 int 而不使用 A 则无法使用 ADL 找到 g

template <typename T>
void f(T i) {
  if (i < 0) {
    g(-i);  // POI 二阶段查找,T 为 A 可以使用 ADL,T 为 int 则找不到 g
  }
}

// 此处不能为 POI,因为 g() 不可见,无法解析 g(-i)
void g(Int) {
  // 此处不能为 POI,不允许在此处插入 f<Int>(Int) 的定义
  f<Int>(42);  // 调用点
  // 此处不能为 POI,不允许在此处插入 f<Int>(Int) 的定义
}
// 是 POI,此时 g() 可见,实例化 f<Int>(Int)

}  // namespace jc

int main() {}
namespace jc {

template <typename T>
struct A {
  T x;
};

// POI
int f() {
  // 不能是 POI,A<int> 的定义不能出现在函数作用域内
  return sizeof(A<int>);
  // 不能是 POI,A<int> 的定义不能出现在函数作用域内
}
// 不能是 POI,如果是 POI 则 sizeof(A<int>) 无效,因为编译后才知道大小

}  // namespace jc

int main() {}
namespace jc {

template <typename T>
struct A {
  using type = int;
};

// A<char> 的 POI
template <typename T>
void f() {
  A<char>::type a = 0;
  typename A<T>::type b = 0;
}

}  // namespace jc

int main() {
  jc::f<double>();
  // A<double> 的 POI
  // f<double> 的 POI
  // f 使用了 dependent name A<T>,需要一个二次 POI
  // 此处有两个 POI,对于类实例,二次 POI 位于主 POI 之前(函数实例则位置相同)
}

模板的链接(Linkage of Template)

namespace jc {

int A;

class A;  // OK:两者名称在不同的空间

int B;

template <typename T>
struct B;  // 错误:名称冲突

struct C;

template <typename T>
struct C;  // 错误:名称冲突

}  // namespace jc

int main() {}
namespace jc {

extern "C++" template <typename T>
void normal();  // 默认方式,链接规范可以省略不写

extern "C" template <typename T>
void invalid();  // 错误:不能使用 C 链接

extern "Java" template <typename T>
void java_link();  // 非标准链接,某些编译器可能支持

}  // namespace jc

int main() {}
template <typename T>  // 与其他文件中同名的声明指向相同的实例
void external();

template <typename T>  // 与其他文件中同名的模板无关
static void internal();

template <typename T>  // 重复声明
static void internal();

namespace {
template <typename>  // 与其他文件中同名的模板无关
void other_internal();
}

namespace {
template <typename>  // 重复声明
void other_internal();
}

struct {
  template <typename T>
  void f(T) {}  // 无链接:不能被重复声明
} x;

int main() {}

链接错误

// a.hpp
#pragma once

namespace jc {

template <typename T>
class A {
 public:
  void f();
};

}  // namespace jc

// a.cpp
#include "a.hpp"

namespace jc {

template <typename T>
void A<T>::f() {}

}  // namespace jc

// main.cpp
#include "a.hpp"

int main() {
  jc::A<int>{}.f();  // 链接错误
}
// a.hpp
#pragma once

namespace jc {

template <typename T>
class A {
 public:
  void f();
};

template <typename T>
inline void A<T>::f() {}

}  // namespace jc

// main.cpp
#include "a.hpp"

int main() { jc::A<int>{}.f(); }

显式实例化(Explicit Instantiation)

// a.hpp
#pragma once

namespace jc {

template <typename T>
class A {
 public:
  void f();
};

extern template class A<int>;         // 声明
extern template void A<double>::f();  // 声明

}  // namespace jc

// a.cpp
#include "a.hpp"

namespace jc {

template <typename T>
void A<T>::f() {}

template class A<int>;  // 实例化 A<int>,同时会实例化其所有成员
template void A<double>::f();  // 仅实例化该成员

}  // namespace jc

// main.cpp
#include "a.hpp"

int main() {
  jc::A<int>{}.f();
  jc::A<double>{}.f();
}
// a.hpp
#pragma once

namespace jc {

template <typename T>
class A {
 public:
  void f();
};

extern template class A<int>;
extern template void A<double>::f();

}  // namespace jc

// a.cpp
#include "a.hpp"

namespace jc {

template <typename T>
void A<T>::f() {}

template class A<int>;
template void A<double>::f();

}  // namespace jc

// a_init.cpp
#include "a.cpp"

namespace jc {

template class A<int>;
template void A<double>::f();

}  // namespace jc

// main.cpp
#include "a.hpp"

int main() {
  jc::A<int>{}.f();
  jc::A<double>{}.f();
}
namespace jc {

template <typename T>
void f(T, T) {}

template void f<double>(double, double);

}  // namespace jc

int main() {
  jc::f<double>(1, 3.14);  // OK
  jc::f(1, 3.14);  // 错误:推断类型不一致,不存在普通函数 f(double, double)
}
namespace jc {

template <typename T>
struct A {
  void f();
};

template <typename T>
void A<T>::f() {}

// template<> struct A<int> { void f() {} };
template struct A<int>;  // 相当于创建如上实例

// template <>
// struct A<int> {};  // 不允许重定义

}  // namespace jc

int main() {}
#include <cassert>
#include <iostream>
#include <string>

namespace jc {

template <auto>
struct A;

template <typename T, typename Class, T Class::*Member>
struct A<Member> {
  friend T& get(Class& c) { return c.*Member; }
};

}  // namespace jc

class Data {
 public:
  std::string value() const { return value_; }

 private:
  std::string value_ = "downdemo";
};

template struct jc::A<&Data::value_>;
std::string& jc::get(Data&);

int main() {
  Data data;
  assert(data.value() == "downdemo");
  jc::get(data) = "june";
  assert(data.value() == "june");
}