C++标准库 - std

【生活经历分享】华师国培 华师伴学 合同都是坑 消费者付款后无法退款
和华师国培签合同需小心,合同中都是保护华师的条款,没有保护消费者的条款。
收到钱,就算你因对培训质量不满意,也不能退款。因合同消费者维权肯定十分艰难。
华师伴学的授课方式是看录制的视频,不是真人现场教学。是否是您和孩子想要的学习方式?
各位打算报名的,交费要谨慎!
其他人在小红书上发的,转:
深圳市华师国培教育科技有限公司,黑心机构,大家擦亮眼睛,别被骗了,消费欺诈,虚假承诺,签合同各种坑,收到钱了不履行承诺不退款,乱扣费,维权艰难! - 小红书


【工作要用到的技术,当时也是网上找的,已经不记得出处。如果有问题请联系我,删除处理】

主要内容:

  1. 标准库包括 stream, string 等,STL 大约占了标准库内容得80%;
  2. C++11包括大量的新特性:包括lambda表达式,类型推导关键字auto、decltype,和模板的大量改进。
  3. STL六大组件简介

 

标准库包括 stream, string 等,STL 大约占了标准库内容得80%;
STL 是标准模板库,是标准库的子集。主要是容器、算法、迭代器;
std 是命名空间的名字,目的是为了避免命名空间污染。
 
C++标准库的所有头文件都没有扩展名。
 
ifstream/fstream 用法可参考: https://cplusplus.com/reference/fstream/
 
uint16_t DataCenter::readInfo(std::string path)
{
std::ifstream ifs(path);
if (!ifs.is_open())
{
LOG_E("Open file [", path,"] failed.");
return 0xFFFF;
}
 
char data[5];
if (ifs.getline(data, 5).fail())
{
LOG_E("Read time format [", path,"] failed.");
return 0xFFFF;
}
uint16_t value = strToUint(data);
ifs.close();
return value;
}
 
 
bool SetInfoManager::writeInfo(std::string path, uint16_t value)
{
std::fstream fs;
fs.open(path, std::fstream::in | std::fstream::out | std::fstream::trunc);
if (!fs.is_open())
{
LOG_E("Open file[", path,"] failed.");
return false;
}
fs << value;
fs.flush();
fs.close();
LOG_I("Saved value[", value, "] in file[", path,"].");
return true;
}
 
 
c++ 输入文件流 ifstream 的继承关系: ios_base <- ios <- istream <- ifstream

 

C++标准库的组成主要包括以下几个部分:
1、核心语言支持(Core Language Support): 提供了对C++核心语言特性的支持,如类型转换、异常处理、动态内存管理等。
2、容器(Containers): 提供了多种容器类模板,如vector、list、map等,用于存储和操作数据集合。
3、算法(Algorithms): 提供了一系列常用的算法,如排序、查找、遍历等,可以直接应用于容器中的数据。
4、迭代器(Iterators): 用于遍历容器中的元素,提供了统一的访问方式。
5、函数对象(Function Objects): 定义了可调用的对象,可以在算法中使用,类似于函数指针。
6、数值计算(Numerics): 提供了数值计算相关的函数和类模板,如数值求解、线性代数、随机数生成等。
7、输入输出(Input/Output): 提供了输入和输出操作的函数和类模板,支持控制台、文件和流的操作。
8、字符串处理(Strings): 提供了字符串的处理函数和类模板,包括字符串查找、替换、转换等操作。
9、日期和时间(Date and Time): 提供了日期和时间的处理函数和类模板,如日期的表示和计算、时间的测量等。
 
STL六大组件简介
1、容器 Containers: 各种数据结构, 如 Vector, List, Deque, Set, Map, 用来存放数据, STL 容器是一种Class Template, 就体积而言, 这一部分很像冰山在海面的比率。
2、算法 Algorithms: 各种常用算法, 如 Sort, Search, Copy, Erase, 从实现的角度来看, STL 算法是一种 Function Templates。
3、迭代器 Iterators: 扮演容器与算法之间的胶合剂, 是所谓的“泛型指针”, 共有五种类型, 以及其它衍生变化, 从实现的角度来看, 迭代器是一种将: Operators*, Operator->, Operator++, Operator--等相关操作予以重载的 Class Template。所有 STL 容器都附带有自己专属的迭代器——是的, 只有容器设计者才知道如何遍历自己的元素, 原生指针 Native pointer 也是一种迭代器。
4、仿函数 Functors: 行为类似函数, 可作为算法的某种策略 Policy,从实现的角度来看, 仿函数是一种重载了 Operator() 的 Class 或 Class Template。一般函数指针可视为狭义的仿函数。
5、配接器适配器 Adapters: 一种用来修饰容器 Containers 或仿函数 Functors 或迭代器 Iterators 接口的东西, 例如: STL 提供的 Queue 和 Stack, 虽然看似容器, 其实只能算是一种容器配接器, 因为它们的底部完全借助 Deque, 所有操作由底层的 Deque 供应。改变 Functor 接口者, 称为 Function Adapter; 改变 Container 接口者, 称为 Container Adapter;改变 Iterator 接口者, 称为 Iterator Adapter。配接器的实现技术很难一言蔽之, 必须逐一分析。
6、分配器 Allocators: 负责空间配置与管理, 从实现的角度来看, 配置器是一个实现了动态空间配置、空间管理、空间释放的 Class Template。
这六大组件的交互关系:
container(容器) 通过 allocator(配置器) 取得数据储存空间
algorithm(算法) 通过 iterator(迭代器)存取 container(容器) 内容
functor(仿函数) 可以协助 algorithm(算法) 完成不同的策略变化
adapter(配接器) 可以修饰或套接 functor(仿函数)。
 
C++标准库的内容分为10类:
C1.语言支持 C2.输入/输出 C3.诊断 C4.一般工具 C5.字符串 C6.容器 C7.迭代器支持 C8.算法 C9.数值操作 C10.本地化
C++标准库头文件见: https://zh.cppreference.com/w/cpp/header
C1 标准库中与语言支持功能相关的头文件 头文件  描述  
 定义宏NULL和offsetof,以及其他标准类型size_t和ptrdiff_t。与对应的标准C头文件的区别是,NULL是C++空指针常量的补充定义,宏offsetof接受结构或者联合类型参数,只要他们没有成员指针类型的非静态成员即可。 
 提供与基本数据类型相关的定义。例如,对于每个数值数据类型,它定义了可以表示出来的最大值和最小值以及二进制数字的位数。 
 提供与基本整数数据类型相关的C样式定义。这些信息的C++样式定义在中 
 提供与基本浮点型数据类型相关的C样式定义。这些信息的C++样式定义在中 
 提供支持程序启动和终止的宏和函数。这个头文件还声明了许多其他杂项函数,例如搜索和排序函数,从字符串转换为数值等函数。它与对应的标准C头文件stdlib.h不同,定义了abort(void)。abort()函数还有额外的功能,它不为静态或自动对象调用析构函数,也不调用传给atexit()函数的函数。它还定义了exit()函数的额外功能,可以释放静态对象,以注册的逆序调用用atexit()注册的函数。清除并关闭所有打开的C流,把控制权返回给主机环境。 
 支持动态内存分配 
 支持变量在运行期间的类型标识 
 支持异常处理,这是处理程序中可能发生的错误的一种方式 
 支持接受数量可变的参数的函数。即在调用函数时,可以给函数传送数量不等的数据项。它定义了宏va_arg、va_end、va_start以及va_list类型 
 为C样式的非本地跳跃提供函数。这些函数在C++中不常用 
 为中断处理提供C样式支持 
C2 支持流输入/输出的头文件 头文件  描述  
< iostream> 支持标准流cin、cout、cerr和clog的输入和输出,它还支持多字节字符标准流wcin、wcout、wcerr和wclog。 
 提供操纵程序,允许改变流的状态,从而改变输出的格式。 
 定义iostream的基类 
 为管理输出流缓存区的输入定义模板类 
 为管理输出流缓存区的输出定义模板类 
 支持字符串的流输入输出 
 支持文件的流输入输出 
 为输入输出对象提供向前的声明 
 支持流输入和输出的缓存 
 为标准流提供C样式的输入和输出 
 支持多字节字符的C样式输入输出 
C3 与诊断功能相关的头文件 头文件 描述 
 定义标准异常。异常是处理错误的方式 
 定义断言宏,用于检查运行期间的情形 
 支持C样式的错误信息 
C4 定义工具函数的头文件 头文件 描述 
 定义重载的关系运算符,简化关系运算符的写入,它还定义了pair类型,该类型是一种模板类型,可以存储一对值。这些功能在库的其他地方使用 
 定义了许多函数对象类型和支持函数对象的功能,函数对象是支持operator()()函数调用运算符的任意对象 
 给容器、管理内存的函数和auto_ptr模板类定义标准内存分配器 
 支持系统时钟函数 
C5 支持字符串处理的头文件 头文件 描述 
 为字符串类型提供支持和定义,包括单字节字符串(由char组成)的string和多字节字符串(由wchar_t组成) 
 单字节字符类别 
 多字节字符类别 
 为处理非空字节序列和内存块提供函数。这不同于对应的标准C库头文件,几个C样式字符串的一般C库函数被返回值为const和非const的函数对替代了 
 为处理、执行I/O和转换多字节字符序列提供函数,这不同于对应的标准C库头文件,几个多字节C样式字符串操作的 一般C库函数被返回值为const和非const的函数对替代了。 
 为把单字节字符串转换为数值、在多字节字符和多字节字符串之间转换提供函数 
C6 定义容器类的模板的头文件 头文件 描述 
 定义vector序列模板,这是一个大小可以重新设置的数组类型,比普通数组更安全、更灵活 
 定义list序列模板,这是一个序列的链表,常常在任意位置插入和删除元素 
 定义deque序列模板,支持在开始和结尾的高效插入和删除操作 
 为队列(先进先出)数据结构定义序列适配器queue和priority_queue 
 为堆栈(后进先出)数据结构定义序列适配器stack 
 map是一个关联容器类型,允许根据键值是唯一的,且按照升序存储。multimap类似于map,但键不是唯一的。 
 set是一个关联容器类型,用于以升序方式存储唯一值。multiset类似于set,但是值不必是唯一的。 
 为固定长度的位序列定义bitset模板,它可以看作固定长度的紧凑型bool数组 
C7 支持迭代器的头文件 头文件 描述 
 给迭代器提供定义和支持 
C8 有关算法的头文件 头文件 描述 
 提供一组基于算法的函数,包括置换、排序、合并和搜索 
 声明C标准库函数bsearch()和qsort(),进行搜索和排序 
 允许在代码中使用and代替&& 
C9 有关数值操作的头文件 头文件 描述 
 支持复杂数值的定义和操作 
 支持数值矢量的操作 
 在数值序列上定义一组一般数学操作,例如accumulate和inner_product 
 这是C数学库,其中还附加了重载函数,以支持C++约定 
 提供的函数可以提取整数的绝对值,对整数进行取余数操作 
C10 有关本地化的头文件 头文件 描述 
 提供的本地化包括字符类别、排序序列以及货币和日期表示。 
 对本地化提供C样式支持
 
 
 
概念学习
 对比:
new std::thread([]()->void {
new std::thread([=]() -> void {
new std::thread([=, this]()->void {
 
std::thread([&](){
std::thread([](int value){
std::thread::id tid = std::this_thread::get_id();
cout << " tid=" << tid<< " " << value <
}, temp).detach();
 
线程对象是不可复制的,只能移动。
  • 线程对象要么被join,要么detach,否则可能会导致崩溃。
thread支持的对象参数类型如下:
  • 普通函数;
  • 成员函数;
  • 函数对象;
  • Lambda表达式。
 
示例一,普通函数:
void foo(int a)
{
    std::cout << a << '\n';
}
 
int main()
{
// Create and execute the thread
std::thread thread(foo, 10); // foo is the function to execute, 10 is the
// argument to pass to it
// Keep going; the thread is executed separately
// Wait for the thread to finish; we stay here until it is done
thread.join();
 
return 0;
}
 
示例二,成员函数:
class Bar
{
public:
void foo(int a)
{
std::cout << a << '\n';
}
};
 
int main()
{
Bar bar;
 
// Create and execute the thread
std::thread thread(&Bar::foo, &bar, 10); // Pass 10 to member function
// The member function will be executed in a separate thread
// Wait for the thread to finish, this is a blocking operation
thread.join();
 
return 0;
}
 
示例三,函数对象:
class Bar
{
public:
void operator()(int a)
{
std::cout << a << '\n';
}
};
 
int main()
{
Bar bar;
 
// Create and execute the thread
std::thread thread(bar, 10); // Pass 10 to functor object
// The functor object will be executed in a separate thread
// Wait for the thread to finish, this is a blocking operation
thread.join();
 
return 0;
}
 
 
示例四,Lambda 表达式:
int main()
{
auto lambda = [](int a) { std::cout << a << '\n'; };
 
// Create and execute the thread
std::thread thread(lambda, 10); // Pass 10 to the lambda expression
// The lambda expression will be executed in a separate thread
// Wait for the thread to finish, this is a blocking operation
thread.join();
 
return 0;
}
 
C++11新特性: http://c.biancheng.net/cplus/11/
 
C++11包括大量的新特性:包括lambda表达式,类型推导关键字auto、decltype,和模板的大量改进。
 
C++11中引入auto第一种作用是为了自动类型推导。
auto 关键字基本的使用语法如下(使用 auto 类型推导的变量必须马上初始化):
auto name = value;
auto 的高级用法
auto 除了可以独立使用,还可以和某些具体类型混合使用,这样 auto 表示的就是“半个”类型,而不是完整的类型。请看下面的代码:
int x = 0;
auto *p1 = &x; //p1 为 int *,auto 推导为 int
auto p2 = &x; //p2 为 int*,auto 推导为 int*
auto &r1 = x; //r1 为 int&,auto 推导为 int
auto r2 = r1; //r2 为 int,auto 推导为 int
下面我们来解释一下:
第 2 行代码中,p1 为 int* 类型,也即 auto * 为 int *,所以 auto 被推导成了 int 类型。
第 3 行代码中,auto 被推导为 int* 类型,前边的例子也已经演示过了。
第 4 行代码中,r1 为 int & 类型,auto 被推导为 int 类型。
第 5 行代码是需要重点说明的,r1 本来是 int& 类型,但是 auto 却被推导为 int 类型,这表明当=右边的表达式是一个引用类型时,auto 会把引用抛弃,直接推导出它的原始类型。
接下来,我们再来看一下 auto 和 const 的结合:
int x = 0;
const auto n = x; //n 为 const int ,auto 被推导为 int
auto f = n; //f 为 const int,auto 被推导为 int(const 属性被抛弃)
const auto &r1 = x; //r1 为 const int& 类型,auto 被推导为 int
auto &r2 = r1; //r1 为 const int& 类型,auto 被推导为 const int 类型
下面我们来解释一下:
第 2 行代码中,n 为 const int,auto 被推导为 int。
第 3 行代码中,n 为 const int 类型,但是 auto 却被推导为 int 类型,这说明当=右边的表达式带有 const 属性时, auto 不会使用 const 属性,而是直接推导出 non-const 类型。
第 4 行代码中,auto 被推导为 int 类型,这个很容易理解,不再赘述。
第 5 行代码中,r1 是 const int & 类型,auto 也被推导为 const int 类型,这说明当 const 和引用结合时,auto 的推导将保留表达式的 const 类型。
最后我们来简单总结一下 auto 与 const 结合的用法:
当类型不为引用时,auto 的推导结果将不保留表达式的 const 属性;
当类型为引用时,auto 的推导结果将保留表达式的 const 属性。
 
auto 的限制
前面介绍推导规则的时候我们说过,使用 auto 的时候必须对变量进行初始化,这是 auto 的限制之一。那么,除此以外,auto 还有哪些其它的限制呢?
1) auto 不能在函数的参数中使用。
这个应该很容易理解,我们在定义函数的时候只是对参数进行了声明,指明了参数的类型,但并没有给它赋值,只有在实际调用函数的时候才会给参数赋值;而 auto 要求必须对变量进行初始化,所以这是矛盾的。
2) auto 不能作用于类的非静态成员变量(也就是没有 static 关键字修饰的成员变量)中。
3) auto 关键字不能定义数组,比如下面的例子就是错误的:
char url[] = "http://c.biancheng.net/";
auto str[] = url; //arr 为数组,所以不能使用 auto
4) auto 不能作用于模板参数,请看下面的例子:
格式化复制
template
class A{
//TODO:
};
 
int main(){
A C1;
A C2 = C1; //错误
return 0;
}
auto 的应用
说了那么多 auto 的推导规则和一些注意事项,那么 auto 在实际开发中到底有什么应用呢?下面我们列举两个典型的应用场景。
  • 使用 auto 定义迭代器
auto 的一个典型应用场景是用来定义 stl 的迭代器。
  • auto 用于泛型编程
auto 的另一个应用就是当我们不知道变量是什么类型,或者不希望指明具体类型的时候,比如泛型编程中。
 
 
decltype(是“declare type”的缩写,译为“声明类型”)
实际上有点像auto的反函数,auto可以让你声明一个变量,而decltype则可以从一个变量或表达式中得到类型。
decltype 可以写成下面的形式:decltype(exp) varname;
int x = 3;
decltype(x) y = x;
exp 注意事项
原则上讲,exp 就是一个普通的表达式,它可以是任意复杂的形式,但是我们必须要保证 exp 的结果是有类型的,不能是 void;
decltype 推导规则
decltype 的用法实际上可以非常复杂。当程序员使用 decltype(exp) 获取类型时,编译器将根据以下三条规则得出结果:
  • 如果 exp 是一个不被括号( )包围的表达式,或者是一个类成员访问表达式,或者是一个单独的变量,那么 decltype(exp) 的类型就和 exp 一致,这是最普遍最常见的情况。
  • 如果 exp 是函数调用,那么 decltype(exp) 的类型就和函数返回值的类型一致。
  • 如果 exp 是一个左值,或者被括号( )包围,那么 decltype(exp) 的类型就是 exp 的引用;假设 exp 的类型为 T,那么 decltype(exp) 的类型就是 T&。
decltype 的实际应用
我们知道,auto 只能用于类的静态成员,不能用于类的非静态成员(普通成员),如果我们想推导非静态成员的类型,这个时候就必须使用 decltype 了。
 
auto 虽然在书写格式上比 decltype 简单,但是它的推导规则复杂,有时候会改变表达式的原始类型;而 decltype 比较纯粹,它一般会坚持保留原始表达式的任何类型,让推导的结果更加原汁原味。
从代码是否健壮的角度考虑,我推荐使用 decltype,它没有那么多是非;但是 decltype 总是显得比较麻烦,尤其是当表达式比较复杂时,例如:
vector nums;
decltype(nums.begin()) it = nums.begin();
而如果使用 auto 就会清爽很多:
vector nums;
auto it = nums.begin();
在实际开发中人们仍然喜欢使用 auto 关键字(我也这么干),因为它用起来简单直观,更符合人们的审美。如果你的表达式类型不复杂,我还是推荐使用 auto 关键字,优雅的代码总是叫人赏心悦目,沉浸其中。
 
nullptr
nullptr是为了解决C++中NULL的二义性问题而引进的一种新的类型,因为NULL实际上代表的是0。
 
序列for循环
在C++中for循环可以使用类似java的简化的for循环,可以用于遍历数组,容器,string以及由begin和end函数定义的序列(即有Iterator),示例代码如下:
map m{{"a", 1}, {"b", 2}, {"c", 3}};
for (auto p : m){
cout<
}
 
Lambda表达式
lambda表达式类似Javascript中的闭包,它可以用于创建并定义匿名的函数对象,以简化编程工作。Lambda的语法如下:
格式说明: auto func = [capture] (params) opt -> ret { func_body; };
[外部变量访问方式说明符] (参数) mutable noexcept/throw() -> 返回值类型
{
   函数体;
};
mutable (opt)
此关键字可以省略,如果使用则之前的 () 小括号将不能省略(参数个数可以为 0)。默认情况下,对于以值传递方式引入的外部变量,不允许在 lambda 表达式内部修改它们的值(可以理解为这部分变量都是 const 常量)。而如果想修改它们,就必须使用 mutable 关键字。
注意,对于以值传递方式引入的外部变量,lambda 表达式修改的是拷贝的那一份,并不会修改真正的外部变量;
noexcept/throw()
可以省略,如果使用,在之前的 () 小括号将不能省略(参数个数可以为 0)。默认情况下,lambda 函数的函数体中可以抛出任何类型的异常。而标注 noexcept 关键字,则表示函数体内不会抛出任何异常;使用 throw() 可以指定 lambda 函数内部可以抛出的异常类型。
值得一提的是,如果 lambda 函数标有 noexcept 而函数体内抛出了异常,又或者使用 throw() 限定了异常类型而函数体内抛出了非指定类型的异常,这些异常无法使用 try-catch 捕获,会导致程序执行失败(本节后续会给出实例)。
定义了一个最简单的 lambda 匿名函数:
[]{}
显然,此 lambda 匿名函数未引入任何外部变量([] 内为空),也没有传递任何参数,没有指定 mutable、noexcept 等关键字,没有返回值和函数体。所以,这是一个没有任何功能的 lambda 匿名函数。
vector iv{5, 4, 3, 2, 1};
int a = 2, b = 1;
for_each(iv.begin(), iv.end(), [b](int &x){cout<<(x + b)<
for_each(iv.begin(), iv.end(), [=](int &x){x *= (a + b);}); // (2)
for_each(iv.begin(), iv.end(), [=](int &x)->int{return x * (a + b);});// (3)
[]内的参数指的是Lambda表达式可以取得的全局变量。
(1)函数中的b就是指函数可以得到在Lambda表达式外的全局变量,如果在[]中传入=的话,即可以取得所有的外部变量(值传递),如(2)和(3)Lambda表达式()内的参数是每次调用函数时传入的参数。
->后加上的是Lambda表达式返回值的类型,如(3)中返回了一个int类型的变量
例1,[] 中无内容:
auto add = [](int a, int b) { return a + b; }; std::cout << add(1, 2) << std::endl;
例2:值&引用传递
[a]
a为值传递
[a, &b]
a为值传递,b为引用传递
[&]
所有变量都用引用传递。当前对象(即this指针)也用引用传递。
[=]
所有变量都用值传递。当前对象用引用传递。
lambda表达式容许捕获必定范围内的变量:
  • []不捕获任何变量
  • [&]引用捕获,捕获外部做用域全部变量,在函数体内看成引用使用
  • [=]值捕获,捕获外部做用域全部变量,在函数内内有个副本使用
  • [=, &a]值捕获外部做用域全部变量,按引用捕获a变量
  • [a]只值捕获a变量,不捕获其它变量
  • [this]捕获当前类中的this指针
 
注意事项
  • 捕获时机
int i = 1;
auto f = [=]() { std::cout << i << std::endl; };
i = 2;
f(); // 输出 1
 
可以看出,在定义Lambda的地方就已经捕获到i的值。后面修改i也不影响f的输出。
如果把[=]改成[&],则会输出2。因为Lambda实际上只捕获到i的引用。
  • 局部变量的生命周期
std::function GetLambda() {
int i = 1;
return [&]() { std::cout << i << std::endl; };
}
auto f = GetLambda();
f(); // 输出 -858993460 之类的乱码
使用引用的方式访问局部变量时,要注意Lambda的生命周期不能超过该局部变量的生命周期。
 
静态断言 static assert
static_assert 可在编译时作判断。例如:
static_assert( size_of(int) == 4 );
 
字符串字面量
const char* a = "string a";
const char* b = u8"string b"; // UTF-8
const char16_t* c = u"string c"; // UTF-16
const char32_t* d = U"string d"; // UTF-32
const char* e = R"(string e1 stirng e2)"; // raw string
 
返回类型后置(trailing-return-type,又称跟踪返回类型)语法,将 decltype 和 auto 结合起来完成返回值类型的推导。
返回类型后置语法是通过 auto 和 decltype 结合起来使用的,语法可以写成:
template auto add(T t, U u) -> decltype(t + u) { return t + u; }
为了进一步说明这个语法,再看另一个例子:
int& foo(int& i); float foo(float& f); template auto func(T& val) -> decltype(foo(val)) { return foo(val); }
 
C++11支持函数模板的默认模板参数
C++98/03 标准中,类模板可以有默认的模板参数,如下:
template struct Foo { // ... };
但是却不支持函数的默认模板参数:
template // error in C++98/03: default template arguments void func() { // ... }
现在这一限制在 C++11 中被解除了。
当所有模板参数都有默认参数时,函数模板的调用如同一个普通函数。但对于类模板而言,哪怕所有参数都有默认参数,在使用时也必须在模板名后跟随<>来实例化。
函数模板的默认模板参数在使用规则上和其他的默认参数也有一些不同,它没有必须写在参数表最后的限制。
template R func(U val) { return val; } int main() { func(97); // R=int, U=int func(97); // R=char, U=int func(97); // R=double, U=int return 0; }
再次强调,当默认模板参数和自行推导的模板参数同时使用时,若无法推导出函数模板参数的类型,编译器会选择使用默认模板参数;如果模板参数即无法推导出来,又未设置其默认值,则编译器直接报错。例如:
template void func(T val1 = 0, U val2 = 0) { //... } int main() { func('c'); //T=char, U=double func(); //编译报错 return 0; }
其中,func('c') 的这种调用方式,编译器通过实参 'c' 可以推导出 T=char,但由于未传递第 2 个实参,因此模板参数 U 使用的是默认参数 double;但 func() 的调用方式是不行的,虽然 val1 设置有默认值,但编译器无法通过该默认值推导出模板参数 T 的类型。由此不难看出,编译器的自动推导能力并没有想象的那么强大。
 
 
 
 
posted @ 2025-07-07 08:45  91program  阅读(81)  评论(0)    收藏  举报