13 实例化

2021-05-29  本文已影响0人  奇点创客

On-Demand 实例化(隐式实例化)

template<typename T>
struct A; // 前置声明

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

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

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

template<typename T>
void A<T>::f() { // A::f() 的定义
}
template<typename T>
struct A {
  A(int);
};

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

int main() {
  f(42); // 前两个函数声明都可以匹配,尽管只调用第二个,仍会实例化第一个
}

延迟实例化(Lazy Instantiation)

template<typename T>
class Safe {
};

template<int N>
class Danger {
  int arr[N]; // N<=0 则失败,但编译器会假设 N 是正整数
};

template<typename T, int N>
class Tricky {
 public:
  void noBodyHere(Safe<T> = 3); // 不一定能用整数对模板 Safe 初始化
  // 但编译器会假设对 Safe<T> 的泛型定义不会用到这个默认实参
  void inclass() {
    Danger<N> noBoomYet; // OK until inclass() is used with N<=0
  }
  void error() { // 会引发错误
    Danger<-1> boom;
  }
  // Danger<-1> 会被要求给出类 Danger<-1> 的完整定义
  // 于是会定义一个 -1 大小的数组
  // 即使 error 没被使用不被实例化,也仍会引发错误
  void unsafe(T (*p)[N]); // 在 N 没被模板参数替换前该声明不会出错
  T operator->();
  // virtual Safe<T> suspect();
  struct Nested {
    Danger<N> pfew; // OK until Nested is used with N<=0
  };
  union { // 匿名 union
    int align;
    Safe<T> anonymous;
  };
};

int main() {
  Tricky<int, 0> ok; // 默认构造函数和析构函数肯定会被调用
  // 因此它们的定义必须存在,虚函数的定义也必须存在
  // 因此 suspect() 只有声明没有定义则会出现链接错误

  // 对于 inclass() 和结构 Nested 的定义,会要求一个 Danger<0> 类型
  // 但因为没有用到这两个成员的定义,因此不会产生定义而引发错误

  // 所有成员声明都会被生成,因此 N 为 0 时 unsafe(T (*p)[N]) 会产生错误
  // 同理,如果匿名 union 中的不是 Safe<T> 而是 Danger<T> 也会产生错误

  // 对于 operator-> 通常应该返回指针类型,或用于这个操作符的 class 类型
  // 但在模板中规则会更灵活,虽然这里 T 为 int,返回 int 类型,但不会出错
}

两阶段查找(Two-Phase Lookup)

POI(Points of Instantiation)

struct A {
  A(int i);
};

A operator-(const A&);
bool operator>(const A&, const A&);

using Int = A;

template<typename T>
void f(T i) {
  if (i > 0) g(-i);
}

// (1) 若此处为 POI,g() 不可见,g(-i) 不能被解析
void g(Int) {
  // (2) 不能是POI,不允许在此处插入 ::f<Int>(Int) 的定义
  f<Int>(42); // 调用点
  // (3) 同 (2)
}
// (4) 此时 g() 可见,POI:实例化 ::f<Int>(Int)
template<typename T>
struct A {
  T x;
};
// (1) POI
unsigned long f() {
  // (2) 不能是 POI,A<int> 的定义不能出现在函数作用域内
  return (unsigned long)sizeof(A<int>);
  // (3) 同 (2)
}
// (4) 如果此处是 POI,则 sizeof(A<int>) 无效,因为编译后才知道大小
template<typename T>
struct A {
  using Type = int;
};

// (1) A<char> 的 POI
template<typename T>
void f() {
  A<char>::Type a = 41;
  typename A<T>::Type b = 42;
}

int main() {
  f<double>();
}
// (2) f<double> 的 POI,但因为 f 使用了依赖型名称 A<T>,还需要一个二次 POI
// 因此此处有两个 POI,对于类实体,二次 POI 位于主 POI 之前(函数实体则位置相同)
// (2)(a) A<double> 的 POI;(2)(b) f<double> 的 POI

显式实例化

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

template void f<int>(int); // 实例化 f<int>(int),无实参推断
template void f<>(float); // 实例化 f<float>(float),有实参推断
template void f(long); // 实例化 f<long>(long),有实参推断
template<typename T>
class A {
 public:
  void f() {}
};

template class A<void>; // 同时显式实例化 template void A<void>::f()
template void A<int>::f(); // 仅显式实例化该成员
template<typename T>
void f() {
}

template void f<int>(); // 显式实例化

template<> // 错误:已经基于int实例化过,不能再对int特化
void f<int>() {
  std::cout << 1;
}
#include <iostream>

template<typename T>
void f() {
}

template<>
void f<int>() {
  std::cout << 1;
}

template void f<int>(); // 显式实例化的是特化的定义

int main() {
  f<int>(); // 1
}
template<typename T>
void f(T, T) {}

template void f<double>(double, double); // 显式实例化

f(1, 3.14); // 错误:推断类型不一致,不存在可匹配的普通函数 void f(double, double)
f<double>(1, 3.14); // OK

显式实例化声明(Explicit Instantiation Declarations)

// t.hpp:
template<typename T> void f() {}

extern template void f<int>(); // 声明但不定义
extern template void f<float>(); // 声明但不定义

// t.cpp:
template void f<int>(); // 定义
template void f<float>(); // 定义

标准库中的显式实例化

// VS2019 头文件 <istream> 中的部分源码

#if defined(_DLL_CPPLIB)

#if !defined(_CRTBLD) || defined(__FORCE_INSTANCE)
template class _CRTIMP2_PURE_IMPORT basic_iostream<char, char_traits<char>>; // __declspec(dllimport)
template class _CRTIMP2_PURE_IMPORT basic_iostream<wchar_t, char_traits<wchar_t>>;
#endif // !defined(_CRTBLD) || defined(__FORCE_INSTANCE)

#ifdef __FORCE_INSTANCE
template class _CRTIMP2_PURE_IMPORT basic_iostream<unsigned short, char_traits<unsigned short>>;
#endif // __FORCE_INSTANCE
#endif // defined(_DLL_CPPLIB)

if constexpr

template<typename T, typename... Ts>
void print(T&& t, Ts&&... ts) {
  std::cout << t << std::endl;
  if constexpr (sizeof...(ts) > 0) {
    print(std::forward<Ts>(ts)...); // 当条件满足时才实例化
  }
}

int main() {
  print(3.14, 42, std::string{ "hello" }, "world");
}
template<bool b>
struct A;

template<typename T, typename... Ts>
void print(T&& t, Ts&&... ts) {
  std::cout << t << std::endl;
  A<(sizeof...(ts) > 0)>::f(std::forward<Ts>(ts)...);
}

template<bool b>
struct A {
  template<typename... Ts>
  static void f(Ts&&... ts) {
    print(std::forward<Ts>(ts)...);
  }
};

template<>
struct A<false> {
  template<typename... Ts>
  static void f(Ts&&... x) {}
};

int main() {
  print(3.14, 42, std::string{ "hello" }, "world");
}
void f();

void g() {
  if constexpr (sizeof(int) == 1) {
    f(); // f() 未定义也不会报错
  }
}
上一篇 下一篇

猜你喜欢

热点阅读