Skip to the content.

函数模板重载

#include <cassert>

namespace jc {

template <typename T>
int f(T) {
  return 1;
}

template <typename T>
int f(T*) {
  return 2;
}

}  // namespace jc

int main() {
  int* p = nullptr;
  assert(jc::f<int*>(p) == 1);
  assert(jc::f<int>(p) == 2);
  assert(jc::f(p) == 2);  // 两个模板均匹配,第二个模板更特殊
  assert(jc::f(0) == 1);  // 0 推断为 int,匹配第一个模板
  assert(jc::f(nullptr) == 1);  // nullptr 推断为 std::nullptr_t,匹配第一个模板
}
#include <cassert>

namespace jc {

template <typename T>
int f(T) {  // 1
  return 1;
}

template <typename T>
int f(T*) {  // 2
  return 2;
}

template <typename T>
int f(const T*) {  // 3
  return 3;
}

}  // namespace jc

int main() {
  const int* p = nullptr;
  assert(jc::f(p) == 3);
  // 推断结果:
  // 1: f(T)        [T = const int*]
  // 2: f(T*)       [T = const int]
  // 3: f(const T*) [T = int]
  // 偏序处理:
  // 用 2 推断 1,T = U*,推断成功
  // 用 1 推断 2,T* = U,无法推断 T
  // 2 比 1 特殊
  // 用 3 推断 1,T = const U*,推断成功
  // 用 1 推断 3,const T* = U,无法推断 T
  // 3 比 1 特殊
  // 用 3 推断 2,T = const U,推断成功
  // 用 2 推断 3,const T = U,无法推断 T
  // 3 比 2 特殊
  // 3 最特殊,因此调用 3
}
#include <iostream>

namespace jc {

struct A {
  A() = default;
  A(const A&) { std::cout << 1; }
  A(A&&) { std::cout << 2; }

  template <typename T>
  A(T&&) {
    std::cout << 3;
  }
};

}  // namespace jc

int main() {
  jc::A a1;
  jc::A a2{a1};  // 3,对 non-const 对象,成员模板优于拷贝构造函数
  jc::A a3{std::move(a1)};  // 2,移动构造函数
  const jc::A b1;
  jc::A b2{b1};             // 1,拷贝构造函数
  jc::A b3{std::move(b1)};  // 3,const A&&,更匹配成员模板
}
namespace jc {

template <typename... Ts>
struct A {};

template <typename T>
constexpr int f(A<T*>) {
  return 1;
}

template <typename... Ts>
constexpr int f(A<Ts...>) {
  return 2;
}

template <typename... Ts>
constexpr int f(A<Ts*...>) {
  return 3;
}

static_assert(f(A<int*>{}) == 1);
static_assert(f(A<int, double>{}) == 2);
static_assert(f(A<int*, double*>{}) == 3);

}  // namespace jc

int main() {}

特化(Specialization)

#include <cassert>

namespace jc {

template <typename T>
int f(T) {  // 1
  return 1;
}

template <typename T>
int f(T*) {  // 2
  return 2;
}

template <>
int f(int) {  // OK:1 的特化
  return 3;
}

template <>
int f(int*) {  // OK:2 的特化
  return 4;
}

}  // namespace jc

int main() {
  int* p = nullptr;
  assert(jc::f(p) == 4);
  assert(jc::f(0) == 3);
  assert(jc::f(nullptr) == 1);
}
namespace jc {

template <typename T>
constexpr int f(T x = 1) {  // T 不会由默认实参推断
  return x;
}

template <>
constexpr int f(int x) {  // 不能指定默认实参
  return x + 1;
}

static_assert(f<int>() == 2);

}  // namespace jc

int main() {}
#include <cassert>

namespace jc {

template <typename T, typename U = int>
struct A;

template <>
struct A<void> {
  constexpr int f();
};

constexpr int A<void>::f() { return 1; }

template <>
struct A<int, int> {
  int i = 0;
};

template <>
struct A<char, char> {
  template <typename T>
  struct B {
    int f() { return i; }
    static int i;
  };
};

template <typename T>
int A<char, char>::B<T>::i = 1;

template <>
int A<char, char>::B<double>::i = 2;

template <>
int A<char, char>::B<char>::f() {
  return 0;
};

// template <>
// struct A<char, char> {};  // 错误,不能对已经特化过成员的类型做特化

template <>
struct A<char, char>::B<bool> {
  int j = 3;
};

}  // namespace jc

int main() {
  static_assert(jc::A<void>{}.f() == 1);
  static_assert(jc::A<void, int>{}.f() == 1);
  // jc::A<void, double>{};  // 错误:未定义类型
  assert((jc::A<int, int>{}.i == 0));
  assert((jc::A<char, char>::B<int>{}.i == 1));
  assert((jc::A<char, char>::B<int>{}.f() == 1));
  assert((jc::A<char, char>::B<double>{}.i == 2));
  assert((jc::A<char, char>::B<double>{}.f() == 2));
  assert((jc::A<char, char>::B<char>{}.i == 1));
  assert((jc::A<char, char>::B<char>{}.f() == 0));
  assert((jc::A<char, char>::B<bool>{}.j == 3));
}
namespace jc {

template <typename T>
struct A {};

A<int> a;

template <>
struct A<double> {};  // OK

template <>
struct A<int> {};  // 错误:不能特化已实例化的 A<int>

}  // namespace jc

int main() {}

偏特化(Partial Specialization)

namespace jc {

template <typename T>
struct A;  // primary template

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

template <typename T>
struct A<T*> {
  static constexpr int size = 0;
};

template <typename T, int N>
struct A<T[N]> {
  static constexpr int size = N;
};

template <typename Class>
struct A<int * Class::*> {
  static constexpr int i = 1;
};

template <typename T, typename Class>
struct A<T * Class::*> {
  static constexpr int i = 2;
};

template <typename Class>
struct A<void (Class::*)()> {
  static constexpr int i = 3;
};

template <typename RT, typename Class>
struct A<RT (Class::*)() const> {
  static constexpr int i = 4;
};

template <typename RT, typename Class, typename... Args>
struct A<RT (Class::*)(Args...)> {
  static constexpr int i = 5;
};

template <typename RT, typename Class, typename... Args>
struct A<RT (Class::*)(Args...) const noexcept> {
  static constexpr int i = 6;
};

struct B {
  int* i = nullptr;
  double* j = nullptr;
  void f1() {}
  constexpr int f2() const { return 0; }
  void f3(int&, double) {}
  void f4(int&, double) const noexcept {}
};

static_assert(A<decltype(&B::i)>::i == 1);
static_assert(A<decltype(&B::j)>::i == 2);
static_assert(A<decltype(&B::f1)>::i == 3);
static_assert(A<decltype(&B::f2)>::i == 4);
static_assert(A<decltype(&B::f3)>::i == 5);
static_assert(A<decltype(&B::f4)>::i == 6);

}  // namespace jc

int main() {
  int a[] = {1, 2, 3};
  static_assert(jc::A<decltype(&a)>::size == 0);
  static_assert(jc::A<decltype(a)>::size == 3);
  // jc::A<const int[3]>{};  // 错误:匹配多个版本
}
#include <cassert>
#include <list>
#include <type_traits>
#include <vector>

namespace jc {

template <typename T>
constexpr int i = sizeof(T);

template <>
constexpr int i<void> = 0;

template <typename T>
constexpr int i<T&> = sizeof(void*);

static_assert(i<int> == sizeof(int));
static_assert(i<double> == sizeof(double));
static_assert(i<void> == 0);
static_assert(i<int&> == sizeof(void*));

// 变量模板特化的类型可以不匹配 primary template
template <typename T>
typename T::iterator null_iterator;

template <>
int* null_iterator<std::vector<int>> = nullptr;

template <typename T, std::size_t N>
T* null_iterator<T[N]> = nullptr;

}  // namespace jc

int main() {
  auto it1 = jc::null_iterator<std::vector<int>>;
  auto it2 = jc::null_iterator<std::list<int>>;
  auto it3 = jc::null_iterator<double[3]>;
  static_assert(std::is_same_v<decltype(it1), int*>);
  assert(!it1);
  static_assert(std::is_same_v<decltype(it2), std::list<int>::iterator>);
  static_assert(std::is_same_v<decltype(it3), double*>);
  assert(!it3);
}