template语法总结(2)

本文深入探讨了C++模板的高级用法,包括std::enable_if的定义与使用、模板特化、偏特化、变参列表的应用以及移动语义等。通过具体的代码示例,帮助读者更好地理解和掌握这些复杂的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近闲来无事,在github上找打了facebook开发的基础库源码,在代码阅读的过程中,看到了很多新颖的template的语法。边看变查,同时记录一下,以免遗忘。

1.  std::enable_if定义

通过跳转其定义的头文件,可以看到标准库中对该模板函数的定义如下。若_Test变量的取值为false则type未定义,否则type定义为_Ty。

	// TEMPLATE CLASS enable_if
template<bool _Test,
	class _Ty = void>
	struct enable_if
	{	// type is undefined for assumed !_Test
	};

template<class _Ty>
	struct enable_if<true, _Ty>
	{	// type is _Ty for _Test
	typedef _Ty type;
	};

所以针对如下测试测试代码结果为:

int main()
{
	// EnableCheck的类型为void,代码可以编过
	using EnableCheck = std::enable_if<std::true_type::value>::type;
	// EnableCheck的类型未定义,代码可以编不过
	// using EnableCheck = std::enable_if<std::false_type::value>::type;

	return 0;
}

2.  template中匿名参数的使用

在template参数定义时,有时使用匿名参数来完成对部分参数条件的校验,只有校验通过才能够正常编译过,否则编译不过,如:

#include <iostream>

template<typename T>
struct NoneInt
{
	static const bool value = true;
};

template<>
struct NoneInt<int>
{
	static const bool value = false;
};

template <typename RetType, typename = std::enable_if<NoneInt<RetType>::value>::type>
RetType Func()
{
	std::cout << "hello" << std::endl;
	return 0;
}

int main()
{
	//可编译过
	Func<float>();
	//可编译不过
	//Func<int>();

	return 0;
}

其中第二个 匿名模板参数就是对返回值的校验,只有返回值为int才能够编译通过。

 

3.  对模板定义特换方式的学习:

模板特化不只是对模板参数列表中的部分(或全部)参数类型指定,以下的方式同样是特化:

template <typename T>
Fuction;

template <typename RetType, typename... Args>
Fuction<RetType(Args...)>
{

}

4.  偏特化仅适用于模板类,不适用于模板函数。而且偏特化中明确指定类型,在构造函数的类型实参中仍需要指定,除非在全模板中有指定默认参数。如下

template <typename E, typename Enabled = void>
struct IsEnumUnderlying : std::false_type
{
};

template <typename E>
struct IsEnumUnderlying<E, typename std::enable_if<std::is_enum<E>::value>::type>
: std::integral_constant<bool, std::is_signed<typename std::underlying_type<E>::type>::value>
{
};

void TestDefaultParam()
{
    enum EnumType {};
    struct NonEnum {};

    // 调用偏特化版本
    IsEnumUnderlying<EnumType> a1;
    // 调用全定义版本
    IsEnumUnderlying<NonEnum> a2;     
}

查看std::enable_if的定义可知,其仅对模板常数取值为true有Type定义,在模板推导时若模板常数取值为false将导致匹配失败。这种方式可用于对前置模板实参的条件限定,即只有同时匹配类型且满足限定条件时才能够完全匹配该模板定义。另外,类模板的构造函数不进行模板形参的类型推导。

5. 模板变参列表的使用

如下函数定义,第一个参数为函数指针,第二个参数为变参列表。在FunctionInvoke中调用该函数,通过forward将参数转发给对应的函数。标准库中的make_shared和thread的构造函数,都是采用与FunctionInvoke类型的定义方式。

template <typename F, typename... Args>
void FunctionInvoke(F f, Args... args)
{
	f(std::forward<Args>(args)...);
}

void printHello(int a)
{
	cout << "Hello" << endl;
}

void TestInvoker()
{
	FunctionInvoke(&printHello, 10);
}

6. 常量定义

template <typename T>
struct IsInt
{
	constexpr static bool value = false;
};

template <>
struct IsInt<int>
{
	constexpr static bool value = true;
};

// 简化模板的使用
template <typename U>
constexpr bool IsInt_v = IsInt<U>::value;

void Test()
{
    //调用方式如下,输出为false
    cout << IsInt_v<float> << endl;
}

7. 移动语义和完美转发

std::move生成的是右值,std::forward是将原来的类型,转发给下一个参数使用(赋值或接口调用)。

 

在学习和掌握C++语言时,理解其语法特性是关键。以下是C++中一些核心语法特性的总结,适用于从基础到高级的学习者,并结合了现代C++的发展趋势。 ### 3.1 命名空间(Namespace) 命名空间用于组织代码,防止全局命名冲突。例如: ```cpp namespace MyNamespace { int value = 42; } int main() { std::cout << MyNamespace::value; // 输出 42 return 0; } ``` 使用命名空间可以有效管理大型项目中的变量、函数和类[^1]。 ### 3.2 引用(Reference) 引用是变量的别名,常用于函数参数传递,避免拷贝带来的性能损耗。例如: ```cpp void print(int& x) { std::cout << x; } int main() { int a = 5; print(a); // 输出 5 return 0; } ``` 引用在C++中被广泛用于提高程序效率和实现操作符重载等高级功能[^1]。 ### 3.3 内联函数(Inline Function) 内联函数通过建议编译器将函数体直接插入调用处来减少函数调用开销。例如: ```cpp inline int max(int a, int b) { return (a > b) ? a : b; } ``` 虽然`inline`关键字是一个提示,但编译器可能忽略它。合理使用内联函数可以提升程序性能[^1]。 ### 3.4 函数重载(Function Overloading) 函数重载允许在同一作用域内定义多个同名函数,只要它们的参数列表不同即可。例如: ```cpp int add(int a, int b) { return a + b; } double add(double a, double b) { return a + b; } ``` 这种机制增强了代码的可读性和复用性,但也需要注意可能导致的歧义问题[^1]。 ### 3.5 模板泛型编程(Template Generic Programming) 模板是C++支持泛型编程的核心机制,允许编写与类型无关的代码。例如: ```cpp template <typename T> T max(T a, T b) { return (a > b) ? a : b; } ``` 模板极大地提升了代码的灵活性和复用能力,在STL库中有广泛应用。 ### 3.6 Lambda 表达式(Lambda Expressions) C++11引入了Lambda表达式,简化了匿名函数的书写。例如: ```cpp #include <algorithm> #include <vector> int main() { std::vector<int> vec = {3, 1, 4}; std::sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; }); return 0; } ``` Lambda表达式常用于算法中的比较逻辑或作为回调函数[^2]。 ### 3.7 智能指针(Smart Pointers) C++11引入了智能指针以帮助管理动态内存,避免内存泄漏。主要类型包括`std::unique_ptr`、`std::shared_ptr`和`std::weak_ptr`。例如: ```cpp #include <memory> int main() { std::unique_ptr<int> ptr(new int(10)); std::cout << *ptr; // 输出 10 return 0; } ``` 智能指针自动处理内存释放,提高了代码的安全性和可靠性[^3]。 ### 3.8 移动语义(Move Semantics) 移动语义通过`std::move`实现资源转移而非拷贝,优化了临时对象的处理。例如: ```cpp std::vector<int> createVector() { std::vector<int> v(1000000, 0); return v; // 使用移动语义返回 } ``` 移动语义显著减少了不必要的深拷贝,提高了性能[^3]。 ### 3.9 范围循环(Range-based for Loop) C++11引入了范围循环,简化了容器遍历。例如: ```cpp #include <vector> int main() { std::vector<int> vec = {1, 2, 3}; for (int val : vec) { std::cout << val << " "; } return 0; } ``` 范围循环使代码更简洁易读,尤其适合处理标准库容器[^3]。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值