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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/743173.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【python】anaconda安装过程

【运行环境】Windows11 文章目录 一、anaconda下载二、anaconda安装三、环境变量配置四、测试环境变量是否配置成功五、总结 一、anaconda下载 1、输入网址“https://www.anaconda.com”进入Anaconda官网。 2、找到【Free Download】点击进入&#xff1a; 3、点击对应系统的…

Redis到底是单线程还是多线程!,【工作感悟】

无论你是做 Python&#xff0c;PHP&#xff0c;JAVA&#xff0c;Go 还是 C#&#xff0c;Ruby 开发的&#xff0c;都离不开使用 Redis。 大部分程序员同学工作中都有用到 Redis&#xff0c;但是只限于会简单的使用&#xff0c;对Redis缺乏整体的认知。 无论是在大厂还是在中小…

数据结构 day2

1:思维导图 2&#xff1a;计算结构体大小 3&#xff1a;大小端存储 &#xff08;1&#xff09;&#xff1a;数据溢出 1 #include <stdio.h>2 #include <string.h>3 #include <stdlib.h>4 int main(int argc, const char *argv[])5 {6 short a 0x1234;…

中国金融统计年鉴、中国保险统计年鉴、中国人口与就业统计年鉴、国民经济和社会发展公报、中国劳动统计年鉴

数据下载链接&#xff1a;百度云下载链接 统计年鉴是指以统计图表和分析说明为主&#xff0c;通过高度密集的统计数据来全面、系统、连续地记录年度经济、社会等各方面发展情况的大型工具书来获取统计数据资料。 统计年鉴是进行各项经济、社会研究的必要前提。而借助于统计年…

一文讲懂 C++ 类和对象(1)

0. 面向过程程序设计和面向对象程序设计的区别 面向对象程序设计往往关注的是怎么去做&#xff0c;是将解决问题的步骤分析出来&#xff0c;然后用函数把步骤一步一步实现&#xff0c;然后再依次调用就可以了。而面向对象是将构成问题的事物&#xff0c;分解成若干个对象&…

Windows达梦数据库(下载及使用)

解压安装包 点击最后一个文件 下一步 接受 下一步 下一步 下一步 点击初始化 开始

Oracle 10g字符编码

pl/sql developer查询数据时出现乱码&#xff0c;主要检查如下&#xff1a; 1、检查服务器编码 select * from v$nls_parameters;select * from nls_database_parameters;select userenv(language) from dual; 2、查看数据库可用字符集参数设置 select * from v$nls_valid_val…

Spring Cloud项目整合Sentinel及简单使用

说明&#xff1a;Sentinel是阿里巴巴开发的微服务治理中间件&#xff0c;可用于微服之间请求的流量管控、权限控制、熔断降级等场景。本文介绍如何在Spring Cloud项目中整合Sentinel&#xff0c;以及Sentinel的简单使用。 环境 首先搭建一个简单的微服务环境&#xff0c;有以…

编程向导-JavaScript-基础语法-块语句

梦里繁花落尽&#xff0c;此情未央&#xff0c;此意难忘&#xff0c;弦虽断&#xff0c;曲犹扬。&#xff01; 与技术共同呼吸&#xff0c;与程序员共命运。世树科技 承“技驱动&#xff0c;理致胜”理念、国风编程。 块语句 块语句&#xff08;Block&#xff09;用于组合零…

深度学习系列62:Agent入门

1 anget介绍和openai标准接口 agent的核心是其代理协同工作的能力。每个代理都有其特定的能力和角色&#xff0c;你需要定义代理之间的互动行为&#xff0c;即当一个代理从另一个代理收到消息时该如何回复。 agent目前大多使用openai标准接口调用LLM服务&#xff0c;说明如下。…

XmlHttpRequest responseType: ‘stream‘ 图片代理服务器

它是一个存在于原生 XMLHttpRequest 对象中的属性。在 Web API 中&#xff0c;XMLHttpRequest 对象用于发送 HTTP 或 HTTPS 请求到服务器&#xff0c;并接收响应。responseType 属性就是用来指定预期从服务器返回的响应数据的类型。 默认值 responseType的默认值为json&#x…

Netty线程模型详解

文章目录 概述单Reactor单线程模型单Reactor多线程模型主从Reactor多线程模型 概述 Netty的线程模型采用了Reactor模式&#xff0c;即一个或多个EventLoop轮询各自的任务队列&#xff0c;当发现有任务时&#xff0c;就处理它们。Netty支持单线程模型、多线程模型和混合线程模型…

linux内建命令/内部命令之caller

1.caller介绍 linux内建命令caller是用来打印函数的调用者信息&#xff0c;需将其放入到函数中才能生效。 2.样例 [rootelasticsearch ~]# cat caller.sh #!/bin/bashfunction1 () {caller 0 }function1 #line 8 [rootelasticsearch ~]# sh caller.sh 8 main caller.sh…

程序员的三重境界:码农,高级码农、程序员!

见字如面&#xff0c;我是军哥&#xff01; 掐指一算&#xff0c;我在 IT 行业摸爬滚打 19 年了&#xff0c;见过的程序员至少大好几千&#xff0c;然后真正能称上程序员不到 10% &#xff0c;绝大部分都是高级码农而已。 今天和你聊聊程序员的三个境界的差异&#xff0c;文章不…

DETR Doesn’t Need Multi-Scale or Locality Design

论文名称&#xff1a;PlainDetr 发表时间&#xff1a;ICCV2023 开源代码 作者及组织&#xff1a; Yutong Lin,Yuhui Yuan等&#xff0c;来自西安交大&#xff0c;微软亚洲研究院。 前言 自Detr以来&#xff0c;后续paper的改进的方向&#xff1a;主要是将归纳偏置重新又引入进…

react的高阶组件怎么用?

在 React 中&#xff0c;高阶组件&#xff08;Higher-Order Component&#xff0c;HOC&#xff09;是一种函数&#xff0c;接受一个组件作为参数&#xff0c;然后返回一个新的增强型组件。高阶组件本质上是一个函数&#xff0c;其目的是重用组件逻辑、增强组件功能以及在不改变…

pinia优化重复请求

目录 1 前言 2 使用步骤 2.1 安装pinia 2.2 定义一个store 2.3 在父组件即index.vue中调用方法 2.4 子组件使用获取到的值 1 前言 在我们的页面中常常存在多个组件&#xff0c;多个组件可能会对某个接口发送多次请求&#xff0c;为了避免这种情况&#xff0c;我们就需要使…

基于左逆的三点法测距,MATLAB函数

基于左逆的三点法MATLAB程序 不一定能用&#xff0c;可以借鉴&#xff0c;在锚点数量极少的时候&#xff0c;右拟无法使用&#xff0c;可以使用这个左逆&#xff08;当然&#xff0c;直接用pinv也行&#xff09; function [p_out] triposition(R_calcu,baseP) % p [5,5;10,…

分布式微服务 - 3.服务网关 - 4.Gateway

分布式微服务 - 3.服务网关 - 4.Gateway 项目示例&#xff1a; 项目示例 - 3.服务网关 - 3.Gateway 内容提要&#xff1a; 基本使用&#xff1a;配置方式、代码方式内置断言、自定义断言内置局部过滤器、自定义内置和全局过滤器 文档&#xff1a; 官网官网文档 基本使用…

重建3D结构方式 | 显式重建与隐式重建(Implicit Reconstruction)

在3D感知领域&#xff0c;包括3D目标检测在内&#xff0c;显式重建和隐式重建是两种不同的方法来表示和处理三维数据。它们各自有优势和局限&#xff0c;适用于不同的场景和需求。 显式重建&#xff08;Explicit Reconstruction&#xff09; 显式重建是指直接构建场景或物体的三…