Skip to the content.

函数模板

#include <cassert>
#include <string>

namespace jc {

template <typename T>
T max(const T& a, const T& b) {
  return a < b ? b : a;
}

}  // namespace jc

int main() {
  assert(jc::max<int>(1, 3) == 3);
  assert(jc::max<double>(1.0, 3.14) == 3.14);
  std::string s1 = "down";
  std::string s2 = "demo";
  assert(jc::max<std::string>(s1, s2) == "down");
}

两阶段编译(Two-Phase Translation)

template <typename T>
void f(T x) {
  undeclared();  // 一阶段编译错误,未声明的函数
  static_assert(sizeof(int) > 10);  // 一阶段,sizeof(int) <= 10,总会编译失败
}

int main() {}
template <typename T>
void f(T x) {
  undeclared(x);  // 调用 undeclared(T) 才会出现函数未声明的实例化错误
  static_assert(sizeof(T) > 10);  // 如果 sizeof(T) <= 10 则实例化错误
}

int main() {
  f(42);  // 调用函数才会进行实例化,不调用则不会有实例化错误
}

模板实参推断(Template Argument Deduction)

#include <cassert>
#include <string>

namespace jc {

template <typename T>
T max(const T& a, const T& b) {
  return a < b ? b : a;
}

}  // namespace jc

int main() {
  assert(jc::max(1, 3) == 3);          // T 推断为 int
  assert(jc::max(1.0, 3.14) == 3.14);  // T 推断为 double
  std::string s1 = "down";
  std::string s2 = "demo";
  assert(jc::max(s1, s2) == "down");  // T 推断为 std::string
}
#include <cassert>

namespace jc {

template <typename T>
T max(const T& a, const T& b) {
  return a < b ? b : a;
}

}  // namespace jc

int main() {
  jc::max(1, 3.14);  // 错误,T 分别推断出 int 和 double,类型不明确
}
#include <cassert>
#include <string>

namespace jc {

template <typename T, typename U>
T max(const T& a, const U& b) {
  return a < b ? b : a;
}

}  // namespace jc

int main() {
  std::string s = "down";
  jc::max("down", s);  // 错误,T 推断为 char[5] 和 std::string
}
#include <cassert>
#include <string>

namespace jc {

template <typename T, typename U>
T max(const T& a, const U& b) {
  return a < b ? b : a;
}

}  // namespace jc

int main() {
  std::string s = "demo";
  assert(jc::max<std::string>("down", "demo") == "down");
  assert(jc::max(std::string{"down"}, s) == "down");
}
#include <cassert>

namespace jc {

template <typename T, typename U>
T max(const T& a, const U& b) {
  return a < b ? b : a;
}

}  // namespace jc

int main() {
  assert(jc::max(1, 3.14) == 3);  // T 推断为 int,返回值截断为 int
  assert(jc::max<double>(1, 3.14) == 3.14);
}
#include <cassert>

namespace jc {

template <typename RT, typename T, typename U>
RT max(const T& a, const U& b) {
  return a < b ? b : a;
}

}  // namespace jc

int main() {
  assert(jc::max<double>(1, 3.14) == 3.14);
  assert((jc::max<double, int, int>(1, 3.14) == 3));
}
#include <cassert>

namespace jc {

template <typename T, typename U>
auto max(const T& a, const U& b) -> decltype(true ? a : b) {
  return a < b ? b : a;
}

}  // namespace jc

int main() { assert(jc::max(1, 3.14) == 3.14); }
namespace jc {

template <typename T, typename U>
constexpr auto max(const T& a, const U& b) {
  return a < b ? b : a;
}

}  // namespace jc

int main() { static_assert(jc::max(1, 3.14) == 3.14); }

type traits

#include <cassert>
#include <type_traits>

namespace jc {

template <typename T, typename U, typename RT = std::common_type_t<T, U>>
RT max(const T& a, const U& b) {
  return a < b ? b : a;
}

}  // namespace jc

int main() { assert(jc::max(1, 3.14) == 3.14); }

重载

#include <cassert>

namespace jc {

int f(int a, int b) { return 1; }

template <typename T, typename U>
int f(const T&, const U&) {
  return 2;
}

}  // namespace jc

int main() {
  assert(jc::f(1, 3) == 1);
  assert(jc::f<double>(1, 3) == 2);
  assert(jc::f<>(1, 3) == 2);
  assert(jc::f(1, 3.14) == 2);
  assert(jc::f(3.14, 1) == 2);
}
#include <cassert>

namespace jc {

template <typename T, typename U>
int f(const T&, const U&) {
  return 1;
}

template <typename RT, typename T, typename U>
int f(const T& a, const U& b) {
  return 2;
}

}  // namespace jc

int main() {
  assert(jc::f(1, 3.14) == 1);
  assert(jc::f<double>(1, 3.14) == 2);
  //   jc::f<int>(1, 3.14);  // 二义性错误
}
#include <cassert>
#include <cstring>
#include <string>

namespace jc {

template <typename T>
T max(T a, T b) {
  return a < b ? b : a;
}

template <typename T>
T* max(T* a, T* b) {
  return *a < *b ? b : a;
}

const char* max(const char* a, const char* b) {
  return std::strcmp(a, b) < 0 ? b : a;
}

}  // namespace jc

int main() {
  int a = 1;
  int b = 3;
  assert(jc::max(a, b) == b);
  assert(jc::max(&a, &b) == &b);

  std::string s1 = "down";
  std::string s2 = "demo";
  assert(jc::max(s1, s2) == "down");
  assert(std::strcmp(jc::max("down", "demo"), "down") == 0);
}
namespace jc {

template <typename T>
const T& f(const char* s) {
  return s;
}

}  // namespace jc

int main() {
  const char* s = "downdemo";
  jc::f<const char*>(s);  // 错误:返回临时对象的引用
}
#include <cstring>

namespace jc {

template <typename T>
const T& max(const T& a, const T& b) {
  return b < a ? a : b;
}

// 新增函数来支持 C-style 参数
const char* max(const char* a, const char* b) {
  return std::strcmp(a, b) < 0 ? b : a;
}

template <typename T>
const T& max(const T& a, const T& b, const T& c) {
  return max(max(a, b), c);  // max("down", "de") 返回临时对象的引用
}

}  // namespace jc

int main() {
  const char* a = "down";
  const char* b = "de";
  const char* c = "mo";
  jc::max<const char*>(a, b, c);  // 错误:返回临时对象的引用
}
#include <cassert>

namespace jc {

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

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

int f(int) { return 2; }

}  // namespace jc

int main() { assert(jc::g(0) == 1); }

用于原始数组与字符串字面值(string literal)的模板

#include <cassert>
#include <cstddef>

namespace jc {

template <typename T, typename U>
constexpr bool less(const T& a, const U& b) {
  return a < b;
}

template <typename T, std::size_t M, std::size_t N>
constexpr bool less(T (&a)[M], T (&b)[N]) {
  for (std::size_t i = 0; i < M && i < N; ++i) {
    if (a[i] < b[i]) {
      return true;
    }
    if (b[i] < a[i]) {
      return false;
    }
  }
  return M < N;
}

}  // namespace jc

static_assert(jc::less(0, 42));
static_assert(!jc::less("down", "demo"));
static_assert(jc::less("demo", "down"));

int main() {}
#include <cstddef>

namespace jc {

template <typename T>
struct A;

template <typename T, std::size_t N>
struct A<T[N]> {
  static constexpr int value = 1;
};

template <typename T, std::size_t N>
struct A<T (&)[N]> {
  static constexpr int value = 2;
};

template <typename T>
struct A<T[]> {
  static constexpr int value = 3;
};

template <typename T>
struct A<T (&)[]> {
  static constexpr int value = 4;
};

template <typename T>
struct A<T*> {
  static constexpr int value = 5;
};

template <typename T1, typename T2, typename T3>
constexpr void test(int a1[7], int a2[], int (&a3)[42], int (&x0)[], T1 x1,
                    T2& x2, T3&& x3) {
  static_assert(A<decltype(a1)>::value == 5);  // A<T*>
  static_assert(A<decltype(a2)>::value == 5);  // A<T*>
  static_assert(A<decltype(a3)>::value == 2);  // A<T(&)[N]>
  static_assert(A<decltype(x0)>::value == 4);  // A<T(&)[]>
  static_assert(A<decltype(x1)>::value == 5);  // A<T*>
  static_assert(A<decltype(x2)>::value == 4);  // A<T(&)[]>
  static_assert(A<decltype(x3)>::value == 4);  // A<T(&)[]>
}

}  // namespace jc

int main() {
  int a[42];
  static_assert(jc::A<decltype(a)>::value == 1);
  extern int x[];  // 传引用时将变为 int(&)[]
  static_assert(jc::A<decltype(x)>::value == 3);  // A<T[]>
  jc::test(a, a, a, x, x, x, x);
}

int x[] = {1, 2, 3};  // 定义前置声明的数组

零初始化(Zero Initialization)

namespace jc {

template <typename T>
constexpr T default_value() {
  T x{};
  return x;
}

template <typename T>
struct DefaultValue {
  constexpr DefaultValue() : value() {}
  T value;
};

template <typename T>
struct DefaultValue2 {
  T value{};
};

static_assert(default_value<bool>() == false);
static_assert(default_value<char>() == 0);
static_assert(default_value<int>() == 0);
static_assert(default_value<double>() == 0);

static_assert(DefaultValue<bool>{}.value == false);
static_assert(DefaultValue<char>{}.value == 0);
static_assert(DefaultValue<int>{}.value == 0);
static_assert(DefaultValue<double>{}.value == 0);

static_assert(DefaultValue2<bool>{}.value == false);
static_assert(DefaultValue2<char>{}.value == 0);
static_assert(DefaultValue2<int>{}.value == 0);
static_assert(DefaultValue2<double>{}.value == 0);

}  // namespace jc

int main() {}