c++ 模版元编程-00

发布时间 2023-05-29 15:59:09作者: 走弧线的神

本系列文章从零开始介绍C++模版元编程,需要有C++基础。

函数模版

template <typename T>
T add(T const a, T const b) {
  return a + b;
}

auto a = add(42, 21);
auto d = add<double>(41.0, 21); // 无法自动推导类型的时候可以显示的指定类型

类模版

template <typename T>
class wrapper {
public:
	wrapper(T const v) : value(v) {}
  T const& get() const { return value; }
private:
  T value;
};

wrapper a(1); // 模版参数推导
wrapper<int> b(2);

成员函数模版

模版类的成员函数

template <typename T>
class comoosition {
public:
  T add(T const a, T const b) {
    return a + b;
  }
};
composition<int> c;
c.add(12,32);

非模版类的成员函数模版

class compotion {
public:
  template <typename T>
  T add(T const a, T const b) {
    return a + b;
  }
};
compostion c;
c.add<int>(12,13);
c.add(1,2);

类模版的成员函数模版

template <typename T>
class wrapper {
pbulic:
  wrapper(T const v) : value(v) {}
  T const& get() const { return value; }
  
  template <typename U>
  U as() const {
    return static_cast<U>(value);
  }
private:
  T value;
};

// 成员函数模板的模板形参必须与类模板的模板形参不同

wrapper<double> a(42.1);
auto d = a.get();
auto b = a.as<int>();

模版参数

类型模版参数

template <typename T> // 不带默认参数
class wrapper {};

template <typename T = int> // 带默认参数
class wrapper {};

template <typename... T> // 可变参数模版
class wrapper {};

非类型模版

template <int V>
class foo {};

template <int V = 42>
class foo {};

template <int... V>
class foo {};
template <typename T, size_t S>
class buffer {
  T data_[S];
public:
  constexpr T const * data() const { return data_; }
  constexpr T& operator[](size_t const index) {
    return data_[index];
  }
  constexpr T const& operator[] (size_t const index) const {
    return data_[index];
  }
};

buffer<int, 10> b1; 
buffer<int, 2*5> b2;

看一种更常见的类型

struct device {
  virtual void output() = 0;
  virtual ~device() {}
};

template <void (*action) ()>
struct smart_device : device {
  void output() override {
    (*action) ();
  }
};

void say_hello_in_english() {
  std::cout << "Hello, world!\n";
}
auto w1 = std::make_unique<smart_device<&say_hello_in_english>>();
w1->output();

双重模版参数

template <typename T> 
class simple_wrapper {
public:
	T value; 
};

template <typename T> 
class fancy_wrapper {
public:
	fancy_wrapper(T const v) :value(v) {}
  
	T const& get() const { return value; }
  
	template <typename U> U as() const {
		return static_cast<U>(value); 
  }
  
private: 
  T value;
};

template <typename T, typename U, template<typename> typename W = fancy_wrapper>
class wrapping_pair {
public:
	wrapping_pair(T const a, U const b) : item1(a), item2(b) { }
	W<T> item1;
  W<U> item2;
};

默认模版参数

template <typename T = int> 
class foo { /*...*/ };

template <typename T = int, typename U = double> 
class bar { /*...*/ };
template <typename T = int, typename U> 
class bar { }; // error, 类模版带默认参数的参数,后面不能跟不带默认参数的参数。
template <typename T = int, typename U> 
void func() {} // OK
template <typename T = int> 
struct foo;

template <typename T = int> // error redefinition // of default parameter
struct foo {};

模版实例化

模版实例化可以是显式的也可以是隐式的。

隐式实例化

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

int main() {
 	foo<int> *x; // 不会实例化
  foo<int> p;  // 会
  foo<int> p1; // 会
  foo<double> *q; // 不会
	return 0;
}
template <typename T>
struct foo {
  static T data;
};

template <typename T> T foo<T>::data = 0;
int main() {
  foo<int> a;
  foo<double> b;
  foo<double> c;
  std::cout << a.data << '\n'; // 0 
  std::cout << b.data << '\n'; // 0 
  std::cout << c.data << '\n'; // 0
  
  b.data = 42;
  
  std::cout << a.data << '\n'; // 0 
  std::cout << b.data << '\n'; // 42 
  std::cout << c.data << '\n'; // 42
}

显示实例化

显示实例化分为显示实例化定义和显示实例化声明。

// 类模版
template class-key template-name <argument-list>

// 函数模版
template return-type name<argument-list> (parameter-list);
template return-type name(parameter-list);
namespace ns {
  template <typename T>
  struct wrapper {
    T value;
  };
  template struct wrapper<int>; // 显示实例化
}
template struct ns::wrapper<double>; // 显示实例化

int main() {
  
}

namespace ns {
  template <typename T>
  T add(T const a, T const b) {
    return a + b;
  }
  
  template int add(int, int);
}
template double ns::add(double, double);