首页 > 编程语言 >突破编程_C++_C++11新特性(模板的改进与细节)

突破编程_C++_C++11新特性(模板的改进与细节)

时间:2024-03-13 17:31:25浏览次数:17  
标签:11 函数 默认 参数 C++ 模板

1 模板右尖括号的改进

在 C++11 之前,模板的解析和实例化过程中,右尖括号 > 的处理有时会导致一些意外的结果,特别是在嵌套模板或模板模板参数中。这是因为 C++ 编译器通常会试图“查看前方”来确定何时结束模板参数的列表,这有时会导致解析错误。

C++11 对模板的右尖括号处理进行了改进,引入了两个新的规则:

  • 列表初始化:当编译器在模板参数列表中遇到 > 时,它会首先尝试将其解释为列表初始化的结束符,而不是模板参数列表的结束符。这有助于解析像 std::vector<std::initializer_list<int>>这样的类型。
  • 模板参数推断:编译器在解析模板参数时,会进行更多的上下文检查来推断正确的解析方式。这有助于减少由于右尖括号导致的解析歧义。

下面是一些示例和解释:

示例 1:列表初始化

在 C++11 之前,如果需要这样声明一个 std::vector,其元素类型为 std::initializer_list<int>:

std::vector<std::initializer_list<int> > vec;

注意这里使用了额外的空格来避免编译器错误地将 > 解释为模板参数列表的结束符。

在 C++11 中,则可以这样写:

std::vector<std::initializer_list<int>> vec;

现在编译器会正确地解析它,而不需要额外的空格。

示例 2:模板模板参数

考虑以下嵌套模板的声明:

template<template<typename> class Outer, typename Inner>  
struct NestedTemplate {  
    Outer<Inner> obj;  
};  
  
template<typename T>  
struct MyTemplate {  
    T value;  
};  
  
int main() {  
    NestedTemplate<MyTemplate, int> nt;  
    return 0;  
}

在 C++11 之前,这样的代码可能会导致解析错误,因为编译器可能会错误地将 MyTemplate, int 解释为 NestedTemplate 的模板参数列表的结束。然而,在 C++11 中,由于引入了新的解析规则,这段代码可以正确编译。

如何启用这些改进

这些改进是 C++11 标准的一部分,因此你只需要确保你的编译器支持 C++11 或更高版本的标准。大多数现代编译器都默认支持 C++11,或者你可以通过编译器的命令行选项来明确指定使用 C++11(例如,在 GCC 和 Clang 中使用 -std=c++11)。

总体而言,C++11 对模板右尖括号的处理进行了改进,使得模板的声明和实例化更加直观和易于理解。通过引入列表初始化和模板参数推断的规则,C++11 减少了由于右尖括号导致的解析错误和歧义。使用 C++11 或更高版本可以利用这些改进来编写更清晰、更健壮的模板代码。

2 模板别名

C++11 的模板别名(Template Aliases)是一个新特性,它允许你为模板类型或模板函数创建一个别名,从而简化复杂模板类型的声明和使用。模板别名不仅提高了代码的可读性和可维护性,还使得模板的使用更加灵活。

2.1 模板类型别名

模板类型别名允许为复杂的模板类型定义一个简洁的名称。这特别适用于那些嵌套或带有大量模板参数的类型。

语法如下:

template<typename... Args>  
using AliasName = OriginalTemplateType<Args...>;

其中,AliasName 是定义的别名,OriginalTemplateType 是原始的模板类型,Args… 是模板参数。

示例:

假设有一个模板类 std::vector,它接受一个类型参数。则可以为这个模板类创建一个别名,以便更简洁地表示特定类型的 vector:

template<typename T>  
using Vec = std::vector<T>;  
  
int main() 
{  
    Vec<int> intVec; // 等同于 std::vector<int> intVec;  
    Vec<double> doubleVec; // 等同于 std::vector<double> doubleVec;  
    return 0;  
}

在这个例子中,Vec 是 std::vector 的一个别名。使用 Vec 可以避免每次都需要写完整的 std::vector 类型,并且使得代码更加清晰。

2.2 模板函数别名

除了模板类型别名外,C++11 还允许为模板函数创建别名。这在处理重载函数或复杂函数签名时特别有用。

语法如下:

template<typename... Args>  
using AliasFunc = ReturnType OriginalFunctionName(Args...);

其中,AliasFunc 是函数别名,OriginalFunctionName 是原始函数名,ReturnType 是函数的返回类型,Args… 是函数的模板参数。

示例:

假设有一个模板函数,它接受两个相同类型的参数并返回它们的和:

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

则可以为这个模板函数创建一个别名:

template<typename T>  
using AddFunc = T(*)(T, T);  
  
int main() 
{  
    AddFunc<int> addInts = add; // addInts 是指向 add 函数的指针  
    int sum = addInts(5, 3); // 调用 add 函数并计算 5 + 3  
    return 0;  
}

在这个例子中,AddFunc 是 add 函数的别名,它是一个函数指针类型。通过 AddFunc<int> addInts = add;,创建了一个指向 add<int> 函数的指针 addInts。然后,可以像调用普通函数一样调用 addInts。

2.3 总结

C++11 的模板别名特性为复杂的模板类型和函数提供了一个简洁的表示方式,提高了代码的可读性和可维护性。通过使用模板别名,可以避免在代码中重复编写冗长的模板类型和函数签名,使得代码更加清晰和易于理解。在实际开发中,可以根据需要灵活运用模板别名来简化复杂的模板用法。

3 默认模板参数

C++11 的默认模板参数允许在模板声明中为模板参数提供默认值。这大大增加了模板的灵活性和易用性。默认模板参数使得模板函数和模板类在调用或实例化时,如果未提供某些模板参数,则可以使用默认值。

3.1 模板函数默认参数

在 C++11 之前,模板函数是不支持默认参数的。然而,从 C++11 开始,这个限制被解除了。现在,可以为模板函数的模板参数提供默认值。

语法:

template<typename T = DefaultType>  
ReturnType functionName(Parameters);

其中,DefaultType 是模板参数 T 的默认值,ReturnType 是函数的返回类型,functionName 是函数名,Parameters 是函数的参数列表。

示例:

#include <iostream>  
#include <typeinfo>  

template<typename T = int>
void printSize(T t = 0) {
	std::cout << "The sizeof " << typeid(T).name() << " is: " << sizeof(t) << std::endl;
}

int main()
{
	printSize(); // 使用默认模板参数和函数参数,输出 int 的大小  
	printSize(1.0); // 使用浮点数推导模板参数,输出 double 的大小  
	printSize<char>('a'); // 指定模板参数为 char,输出 char 的大小  
	return 0;
}

上面代码的输出为:

The sizeof int is: 4
The sizeof double is: 8
The sizeof char is: 1

在上面的示例中,printSize 函数有一个默认模板参数 T,其默认值为 int。同时,该函数还有一个默认函数参数 t,其默认值为 0。这使得调用者可以选择性地提供模板参数和函数参数。

3.2 模板类默认参数

与模板函数类似,模板类也可以有默认模板参数。这对于创建更加通用且易于使用的模板类非常有用。

语法:

template<typename T1 = DefaultType1, typename T2 = DefaultType2, ...>  
class ClassName {  
    // 类成员和方法  
};

其中,DefaultType1、DefaultType2 等是模板参数 T1、T2 等的默认值,ClassName 是类名。

示例:

#include <iostream>  
#include <typeinfo>
#include <string>

template<typename T1 = int, typename T2 = double>  
class Pair {  
public:  
    T1 first;  
    T2 second;  
  
    Pair(T1 f = T1(), T2 s = T2()) : first(f), second(s) {}  
};  
  
int main() 
{  
    Pair<> p1; // 使用默认模板参数,T1 为 int,T2 为 double  
    Pair<std::string> p2; // 使用 std::string 作为 T1,T2 为默认的 double  
    Pair<char, long> p3('c', 123L); // 明确指定两个模板参数  
    return 0;  
}

在上面的示例中,Pair 类有两个默认模板参数 T1 和 T2,分别默认为 int 和 double。类的构造函数也有默认参数,使得对象的创建更加灵活。

3.3 模板类默认参数在模板类特化中的应用

在 C++ 中,模板类特化是一种为特定类型或类型组合提供模板类的特殊实现的技术。特化可以用于优化性能、添加特定类型的特定行为或提供类型无关的默认实现之外的特定实现。当与 C++11 的默认模板参数结合使用时,模板类特化可以变得更加灵活和强大。

默认模板参数允许在模板声明时为模板参数提供默认值。这样,在实例化模板类时,如果没有提供某些模板参数,编译器会使用默认值。当与模板类特化结合时,可以创建出既具有通用性又具有特定类型优化的代码结构。

以下是一些结合使用默认参数和特化应用场景:

(1)提供默认行为: 同时为特定类型优化:通过默认模板参数,可以为模板类提供一个通用的默认实现。然后,通过特化这个模板类,可以为特定类型提供优化的实现。

(2)处理复杂类型组合: 当模板类涉及多个类型参数时,可以使用部分特化来处理特定的类型组合。结合默认参数,可以进一步减少需要特化的情况,使代码更加简洁。

(3)扩展模板类的功能: 通过特化,可以为模板类添加新的成员函数或修改现有成员函数的行为。结合默认参数,可以确保在没有特化的情况下,模板类仍然具有可用的默认实现。

如下为样例代码:

#include <iostream>  
#include <string>  

// 模板类定义,带有默认模板参数  
template<typename T1 = int, typename T2 = std::string>
class MyClass {
public:
	void print() {
		std::cout << "Default implementation for MyClass<" << typeid(T1).name() << ", " << typeid(T2).name() << ">" << std::endl;
	}
};

// 模板类特化,针对特定类型组合的优化实现  
template<>
class MyClass<int, int> {
public:
	void print() {
		std::cout << "Specialized implementation for MyClass<int, int>" << std::endl;
	}
};

int main() 
{
	// 使用默认模板参数实例化 MyClass  
	MyClass<> obj1;
	obj1.print(); // 输出默认实现  

	// 使用特定类型组合实例化 MyClass,触发特化  
	MyClass<int, int> obj2;
	obj2.print(); // 输出特化实现  

	// 使用其他类型组合实例化 MyClass  
	MyClass<double, std::string> obj3;
	obj3.print(); // 输出默认实现,因为没有针对这种类型组合的特化  

	return 0;
}

上面代码的输出为:

Default implementation for MyClass<int, class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >>
Specialized implementation for MyClass<int, int>
Default implementation for MyClass<double, class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >>

在上面的示例中,MyClass 是一个带有两个默认模板参数的模板类。当使用默认参数实例化时,会调用默认实现。然而,当使用 int 和 int 作为类型参数时,会触发特化版本的 MyClass,从而调用优化后的 print 方法。对于其他类型组合,仍然使用默认实现。

3.4 总结

C++11 的默认模板参数特性使得模板函数和模板类的使用更加简洁和灵活。通过为模板参数提供默认值,可以减少模板实例化时的冗余代码,并提高代码的可读性和可维护性。在实际开发中,合理利用默认模板参数可以极大地提高编程效率。

标签:11,函数,默认,参数,C++,模板
From: https://blog.csdn.net/h8062651/article/details/136674925

相关文章

  • 【C++】thread 头文件无法正常使用问题
    问题当我使用MinGWGCC,在windows上编写C++程序的时候,使用thread类会无法编译,有如下错误:我使用的是c++17这个问题原因是MinGWGCC当前仍缺少标准C++11及以上版本线程类的实现。解决方案Stackoverflow上的类似问题:传送门1.下载源文件:源文件外网可能比较慢,提供C......
  • C++多态和虚函数
    C++多态和虚函数#include<iostream>usingnamespacestd;//基类PeopleclassPeople{public:People(char*name,intage);voiddisplay();protected:char*m_name;intm_age;};People::People(char*name,intage):m_name(name),m_age(age){}......
  • C++初阶:1_C++入门
    C++入门零.本节知识点安排目的C++是在C的基础之上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式等。熟悉C语言之后,对C++学习有一定的帮助,本章节主要目标:补充C语言语法的不足,以及C++是如何对C语言设计不合理的地方进行优化的,比如:作用域方面、IO方面、函数......
  • win11安装wsl2没有网络解决方法
    1、启用hyper-v2、打开hyper-v管理器-点击虚拟交换机管理器-先看下有无一个名为WSL(这个名字可以被修改,和下面对应就行)的虚拟交换机,有的话先设置为外部网络3、编辑%USERPROFILE%.wslconfig添加如下内容[wsl2]networkingMode=bridgedvmSwitch=WSLipv6=true12344、执行wsl-......
  • 19113133262(微信同号)2024年环境能源与全球市场营销国际学术会议(ICEEGM 2024)
    2024年环境能源与全球市场营销国际学术会议(ICEEGM2024)会议主题:(主题包括但不限于,更多主题请咨询会务组苏老师)节能技术煤矿工程与技术能源存储技术可再生能源热能与动力工程 能源工程与环境工程 可再生能源技术和系统能源安全和清洁利用 矿产资源与采矿工......
  • 练习4-11 统计素数并求和
    探索--题目集索引本题要求统计给定整数M和N区间内素数的个数并对它们求和。输入格式:输入在一行中给出两个正整数M和N(1≤M≤N≤500)。输出格式:在一行中顺序输出M和N区间内素数的个数以及它们的和,数字间以空格分隔。输入样例:1031输出样例:7143实现代码:#includ......
  • 浅淡 C++ 与 C++ 入门
            我们知道,C语言是结构化和模块化的语言,适用于较小规模的程序。而当解决复杂问题,需要高度抽象和建模时,C语言则不合适,而C++正是在C的基础之上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式。        因此在这篇博客,将会介绍C++中引入哪些......
  • c++内建函数对象
    概念:c++STL中内建了一些函数对象分类:算术仿函数关系仿函数逻辑仿函数用法:这些仿函数所产生的对象,用法和一般函数完全相同使用内建函数对象,需要使用头文件#include<functional> 1.算术仿函数 功能描述:实现四则运算其中negate是一元运算,其他都是二元运算仿函数......
  • 11-Rigid_body_engine
    ourphysicsengineisnowcapableofsimulatingfullrigidbodiesinfull3D.Thespringforcesandotherforcegeneratorswillworkwiththisapproach,butthehardconstraintswillnot.AFlightSimulatorAsthearicraft'ssurfacesmoveatdifferent......
  • C++ cout的使用总结
    cout是C++中ostream类型的对象cout是C++中ostream类型的对象,该类被封装在<iostream>库中,该库定义的名字都在命名空间std中,所以cout全称是std::cout。1、cout支持多种数据类型,如int、float、double、char、string等,它们都会被自动转换成字符串进行输出。#includ......