現代C++(Modern C++)基本用法實踐:四、模板

2023-07-17 12:00:42

概述

C++的模板是泛型程式設計思想的一種實現。C++是強型別語言,處處強調型別。同樣的加法運算,int和float的加法運算需定義兩個函數(過載),而使用模板則可以只用一個函數(見下面範例)。
這類似我們物件導向所說的多型(定義加法運算,各個型別有不同的實現),所以是所謂靜多型的一種實現方式,不同的是,模板在編譯期展開生成int和float兩個加法函數,如:

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

int v1 = add<int>(1, 2);

// 不顯式宣告模板引數型別,編譯器會試圖推斷
float v2 = add(1.5f, 2.5f);

/*
實際上編譯器生成了兩個函數
int add<int>(int a, int b)
float add<float>(float a, float b)
*/

模板不光支援函數模板,還有類別範本等,思想是一樣的(詳情見下面例子)。
模板還有一些特性機制如:模板特化,SFINAE(substitution failure is not an error 替換而非錯誤),變長引數模板等,另外模板在超程式設計中也是十分重要的組成部分,我對超程式設計沒有太多實踐,讀者有興趣可以自行搜尋。

用法舉例

參考測試專案ModernCppTest/modrenc_template.cpp
主要內容:

  • 函數模板&Lambda函數模板
  • 類別範本
  • 別名模板
  • 變數模板
  • 值(列舉)作為模板引數(其實int型別也可以)
  • 模板特化
  • 變長引數模板
  • 模板函數的完美轉發
#include "ModernCppTestHeader.h"

namespace n_template{

	template <class T>
	void template_func(T t)
	{
		LOG_VAR(t);
	}

	template <class T>
	class Number
	{
	public:
		T v;
		Number(T v) : v(v)
		{
			LOG_VAR(v);
		}
	};


	template <class T>
	constexpr T pi = T(3.1415926f);

	template <int INTVAL>
	void log_template_int_value()
	{
		LOG_VAR(INTVAL);
	}


	enum class EAnim : int {
		Other = 0,
		Cat = 1,
		Dog = 2,
	};

	template<EAnim Ty>
	class Anim
	{
	public:
		void Bark() { LOG("預設:動物叫"); }
	};

	template<>
	class Anim<EAnim::Dog>
	{
	public:
		void Bark() { LOG("狗:汪汪!"); }
	};

	template<>
	class Anim<EAnim::Cat>
	{
	public:
		void Bark() { LOG("貓:喵喵!"); }
	};


	// 注意遞迴基
	void log_values()
	{
		LOG("展開結束");
	}

	template<class T, class... ARGS>
	void log_values(T value, ARGS... args)
	{
		LOG(value);
		log_values(args...);
	}

	template<class T>
	void func_plus(T&& a)
	{
		auto v = a;
		LOG("func_plus v = " << a);
	}

	template<class T, class... ARGS>
	void func_plus(T&& a, T&& b, ARGS... args)
	{
		func_plus(a + b, std::forward<ARGS>(args)...);
	}

	template<class T>
	void func_mul(T&& a)
	{
		auto v = a;
		LOG("func_mul v = " << a);
	}

	template<class T, class... ARGS>
	void func_mul(T&& a, T&& b, ARGS... args)
	{
		func_mul(a + b, std::forward<ARGS>(args)...);
	}

	template <class... ARGS>
	void call_func(const std::string& name, ARGS&&... args)
	{
		if (name == "plus")
			func_plus(std::forward<ARGS>(args)...);
		else if (name == "mul")
			func_mul(std::forward<ARGS>(args)...);
		else
			LOG("Unknown function name: " << name);
	}
}


template<typename T>
using Num = n_template::Number<T>;

void template_test()
{
	LOG_FUNC();

	LOG_TAG(" 函數模板 ");
	{
		n_template::template_func(1);
		n_template::template_func(1.25f);

		LOG("Lambda 函數模板");
		auto f = []<class T> (T t) { LOG_VAR(t); };
		f(1);
		f(1.25f);
	}

	LOG_TAG("類別範本");
	{
		n_template::Number(1);
		n_template::Number(1.25f);
	}

	LOG_TAG("別名模板");
	{
		Num<int>(1);
		Num<float>(1.25f);
	}

	LOG_TAG("變數模板");
	{
		auto v1 = n_template::pi<int>;
		auto v2 = n_template::pi<float>;
		LOG_VAR(v1);
		LOG_VAR(v2);

		n_template::log_template_int_value<10>();
		n_template::log_template_int_value<20>();
	}

	LOG_TAG("列舉變數模板&模板特化");
	{
		n_template::Anim<n_template::EAnim::Dog>().Bark();
		n_template::Anim<n_template::EAnim::Cat>().Bark();
		LOG("EAnim::Other 沒有特化使用預設模板");
		n_template::Anim<n_template::EAnim::Other>().Bark();
	}

	LOG_TAG("變長引數模板");
	{
		n_template::log_values("jack", 10, 3.14f);
	}

	LOG_TAG("變長引數模板的完美轉發");
	{
		n_template::call_func("plus", 1, 2, 3, 4);
		n_template::call_func("mul", 2, 3);
	}
}