C++模板基础3——模板参数,成员模板,控制实例化

模板参数

什么是模板参数

模板参数是在C++中使用模板时,用于指定模板的参数的一种机制。模板参数可以是类型参数、非类型参数或模板参数。

  • 类型参数是指在模板中使用的特定类型,可以是内置类型(如int、float等)、自定义的类类型或其他模板类型。
  • 非类型参数是指在模板中使用的常量值,可以是整数、字符、枚举或指针。

模板参数可以用于定义模板函数、模板类或模板别名。通过在模板定义中指定参数的类型或值,可以根据实际需求生成特定的函数或类。

例如,以下是一个模板函数的示例,其中T是类型参数,n是非类型参数:

template <typename T, int n>
void printArray(T arr[n]) {for (int i = 0; i < n; i++) {cout << arr[i] << " ";}cout << endl;
}

在使用该模板函数时,需要指定类型参数T和非类型参数n,例如:

int main() {int arr[] = {1, 2, 3, 4, 5};printArray<int, 5>(arr);return 0;
}

在上述示例中,类型参数T被指定为int,非类型参数n被指定为5,从而生成一个可以打印整型数组的函数。

类似函数参数的名字,一个模板参数的名字也没有什么内在含义。

我们通常将类型参数命名为T,但实际上我们可以使用任何名字:

template <typename Foo> Foo calc(const Foo& a, const Foo& b)
{
Foo tmp = a; // tmp的类型与参数和返回类型一样
//...
return tmp: //返回类型和参数类型一样
}

模板参数与作用域

模板参数遵循普通的作用域规则。

一个模板参数名的可用范围是在其声明之后,至模板声明或定义结束之前。

与任何其他名字一样,模板参数会隐藏外层作用域中声明的相同名字。

但是,与大多数其他上下文不同,在模板内不能重用模板参数名:

typedef double A;
template <typename A, typename B> void f (A a, B b)
{
A tmp = a; // tmp的类型为模板参数A的类型,而非double
double B; // 错误:重声明模板参数 B
}

正常的名字隐藏规则决定了A的typedef 被类型参数A 隐藏。因此,tmp不是一个double,其类型是使用f时绑定到类型参数A的类型。

由于我们不能重用模板参数名,声明名字为B的变量是错误的。
由于参数名不能重用,所以一个模板参数名在一个特定模板参数列表中只能出现一次:

//错误:非法重用模板参数名V
template <typename V, typename V> //

模板声明

模板声明必须包含模板参数:


// 声明但不定义 compare和Blob 
<typename T>int compare(const T&, const T&);
template <typename T> class Blob;

声明中的模板参数的名字不必与定义中相同:

// 3个calc都指向相同的函数模板
template <typename T>T calc(const T&, const T&);// 声明
template <typename U> U calc(const U&, const U&); //声明// 模板的定义
template <typename Type>
Type calc(const Type& a, const Type& b) { /*... */}


当然,一个给定模板的每个声明和定义必须有相同数量和种类(即,类型或非类型)的参数。

一个特定文件所需要的所有模板的声明通常一起放置在文件开始位置,出现于Best
任何使用这些模板的代码之前

使用类的类型成员

回忆一下,我们用作用域运算符(::)来访问static成员和类型成员。

在普通(非模板)代码中,编译器掌握类的定义。因此,它知道通过作用域运算符访问的名字是类型还是static成员。

例如,如果我们写下string::size_type,编译器有string的定义,从而知道size type是一个类型。 

但对于模板代码就存在困难。

例如,假定工是一个模板类型参数,当编译器遇到类似T::mem这样的代码时,它不会知道mem是一个类型成员还是一个static数据成员,直至实例化时才会知道。

但是,为了处理模板,编译器必须知道名字是否表示一个类型。

例如,假定T是一个类型参数的名字,当编译器遇到如下形式的语句时:

T::size_type * p;

它需要知道我们是正在定义一个名为p的变量还是将一个名为size_type的static数据成员与名为p的变量相乘。

默认情况下,C++语言假定通过作用域运算符访问的名字不是类型。

因此,如果我们希望使用一个模板类型参数的类型成员,就必须显式告诉编译器该名字是一个类型。

我们通过使用关键字typename来实现这一点:

template <typename T>
typename T::value type top(const T& c)
{
if (!c.empty())
return c.back();
else 
return typename T::value_type();
}

我们的top函数期待一个容器类型的实参,它使用typename指明其返回类型并在c中没有元素时生成一个值初始化的元素返回给调用者。

当我们希望通知编译器一个名字表示类型时,必须使用关键字typename,而不能使用class。

默认模板实参

就像我们能为函数参数提供默认实参一样,我们也可以提供默认模板实参。

在新标准中,我们可以为函数和类模板提供默认实参。而更早的C++标准只允许为类模板提供默认实参。

例如,我们重写compare,默认使用标准库的less函数对象模板:

// compare有一个默认模板实参1ess<T>和一个默认函数实参F()
template <typename T, typename F=less<T>>
int compare(const T &vl, const T &v2, F f=F())
{
if (f(v1, v2)) return -1;
if (f(v2, v1)) return 1;
return 0;
}

在这段代码中,我们为模板添加了第二个类型参数,名为F,表示可调用对象的类型;并定义了一个新的函数参数f,绑定到一个可调用对象上。

我们为此模板参数提供了默认实参,并为其对应的函数参数也提供了默认实参。

默认模板实参指出 compare 将使用标准库的 less函数对象类,它是使用与compare一样的类型参数实例化的。默认函数实参指出f将是类型F的一个默认初始化的对象。

当用户调用这个版本的compare时,可以提供自己的比较操作,但这并不是必需的:

bool i = compare(0,42);//使用less;i为-1
// 结果依赖于iteml 和item2 中的isbn
Sales_data iteml (cin), item2 (cin);
bool j = compare(iteml, 1tem2, compareIsbn);


第一个调用使用默认函数实参,即,类型less<T>的一个默认初始化对象。

在此调用中,T为int,因此可调用对象的类型为less<int>。compare的这个实例化版本将使用less<int>进行比较操作。

在第二个调用中,我们传递给compare三个实参:compareIsbn 和两个Sales_data类型的对象。当传递给compare三个实参时,第三个实参的类型必须是一个可调用对象,该可调用对象的返回类型必须能转换为bool值,且接受的实参类型必须与compare的前两个实参的类型兼容。

与往常一样,模板参数的类型从它们对应的函数实参推断而来。在此调用中,T的类型被推断为Sales data,F被推断为compareIsbn的类型。

与函数默认实参一样,对于一个模板参数,只有当它右侧的所有参数都有默认实参时,它才可以有默认实参。

模板默认实参与类模板

无论何时使用一个类模板,我们都必须在模板名之后接上尖括号。

尖括号指出类必须从一个模板实例化而来特别是,如果一个类模板为其所有模板参数都提供了默认实参,且我们希望使用这些默认实参,就必须在模板名之后跟一个空尖括号对:

template <class T = int> class Numbers { // T默认为int
public:
Numbers(T v = 0): val(v) ()
// 对数值的各种操作
private:
T val;
Numbers<long double> lots of_precision;
Numbers<> average_precision; // 空<>表示我们希望使用默认类型

此例中我们实例化了两个Numbers版本:average_precision是用int代替T实例化得到的;lots of precision是用long double代替T实例化而得到的。

成员模板

一个类(无论是普通类还是类模板)可以包含本身是模板的成员函数。这种成员被称为成员模板(member template)。成员模板不能是虚函数。

普通(非模板)类的成员模板

我们直接看例子

下面是一个简单的例子,其中定义了一个名为MyClass的类,它有一个成员函数模板printValue(),可以打印任何类型的值:

#include <iostream>class MyClass {
public:// 成员函数模板定义template <typename T>void printValue(T value) {std::cout << "Value: " << value << std::endl;}
};int main() {MyClass obj;obj.printValue(5);        // 输出:Value: 5obj.printValue(3.14);    // 输出:Value: 3.14obj.printValue("Hello");  // 输出:Value: Helloreturn 0;
}


在这个例子中,printValue()函数可以接受任何类型T的参数,并将其打印出来。

在C++中,成员函数模板可以在类内定义,也可以在类外定义。下面是这两种定义方式的详细解释:

类内定义

在类内部直接定义成员函数模板是非常方便和直观的。这种方式通常用于较短的函数实现,以便将函数的声明和实现紧密地结合在一起。

class MyClass {
public:// 成员函数模板的类内定义template <typename T>void printValue(T value) {std::cout << "Value: " << value << std::endl;}
};


在类内定义时,模板参数和函数体都直接写在类定义中。这种方式适用于函数体较短、逻辑简单的情况。

类外定义

如果成员函数模板的实现比较复杂,或者为了保持类定义的简洁性,你可能希望在类外部定义成员函数模板。在类外定义时,你需要使用作用域解析运算符(::)来指明这个函数属于哪个类。

class MyClass {
public:// 成员函数模板的声明template <typename T>void printValue(T value);
};// 成员函数模板的类外定义
template <typename T>
void MyClass::printValue(T value) {std::cout << "Value: " << value << std::endl;
}


在类外定义时,你首先在类内部声明成员函数模板,然后在类外部提供具体的实现。注意,在类外定义成员函数模板时,template 关键字和模板参数列表仍然需要放在函数实现之前,并且要使用类名和作用域解析运算符来指明这是哪个类的成员函数。

注意事项

  • 无论是类内定义还是类外定义,成员函数模板都是在编译时展开的,针对每种不同的类型,编译器都会生成相应的函数实例。
  • 当你在类外定义成员函数模板时,需要确保模板的定义在使用它的每个编译单元中都是可见的。这通常意味着你需要将成员函数模板的定义放在头文件中,以便多个源文件可以包含它。
  • 如果成员函数模板只在类内部使用,并且实现较短,类内定义是更简洁的选择。如果函数实现较长或者逻辑复杂,类外定义可能更为合适。

类模板的成员模板

类模板的成员模板是指在类模板内部定义的成员函数模板或成员类模板。这种结构提供了两层的模板化:外层是类模板,它允许你以类型参数化的方式定义类;内层是成员模板,它允许类的成员函数或内部类也接受类型参数。

类模板的成员函数模板

当你在一个类模板中定义一个成员函数模板时,你实际上是在为一个已经模板化的类添加了一个可以接受额外类型参数的函数。下面是一个简单的例子:

#include <iostream>
#include <vector>// 类模板定义
template<typename C>
class MyClass {
public:// 成员函数模板声明template<typename T>void process(const std::vector<T>& data) {for (const auto& item : data) {std::cout << "Processing " << item << std::endl;}}// 普通的成员函数,用于对比void process(const std::vector<C>& data) {for (const auto& item : data) {std::cout << "Processing class template type " << item << std::endl;}}
};int main() {MyClass<int> myIntClass;std::vector<int> ints = {1, 2, 3, 4, 5};std::vector<double> doubles = {1.1, 2.2, 3.3, 4.4, 5.5};// 调用成员函数模板myIntClass.process(doubles); // 使用成员函数模板处理double类型的vector// 调用普通的成员函数myIntClass.process(ints); // 使用普通的成员函数处理int类型的vectorreturn 0;
}


在这个例子中,MyClass 是一个类模板,它有一个类型参数 C。类内部定义了一个成员函数模板 process,它接受一个类型为 std::vector<T> 的参数。这意味着你可以为 process 函数传递任何类型的 std::vector,而不仅限于类模板参数 C 指定的类型。

类模板的成员类模板

除了成员函数模板,你还可以在类模板内部定义成员类模板。这种情况比较少见,但在某些复杂的设计中可能是有用的。

template<typename Outer>
class OuterClass {
public:template<typename Inner>class InnerClass {public:Inner value;InnerClass(Inner val) : value(val) {}void print() const {std::cout << "Inner value: " << value << std::endl;}};
};int main() {OuterClass<int>::InnerClass<double> inner(3.14);inner.print(); // 输出:Inner value: 3.14return 0;
}


在这个例子中,OuterClass 是一个类模板,它有一个成员类模板 InnerClass。InnerClass 也是一个模板,可以独立于 OuterClass 的模板参数进行实例化。

总的来说,类模板的成员模板提供了极高的灵活性,允许你在不同类型的上下文中以类型安全的方式重用代码。然而,它们也增加了代码的复杂性,因此需要谨慎使用。

类模板的成员模板也可以在类内定义或在类外定义。下面是这两种定义方式的详细解释:

类内定义

在类模板内部直接定义成员模板是非常方便的。这种方式通常用于较短的函数或类实现,以保持代码的紧凑性和可读性。

成员函数模板的类内定义
template<typename TClass>
class MyClass {
public:// 成员函数模板的类内定义template<typename T>void printValue(T value) {std::cout << "Value of type " << typeid(T).name() << ": " << value << std::endl;}
};


在这个例子中,printValue 是一个成员函数模板,它在类模板 MyClass 内部定义。这种方式使得函数的实现与声明紧密结合,便于阅读和维护。

成员类模板的类内定义
template<typename TOuter>
class OuterClass {
public:// 成员类模板的类内定义template<typename TInner>class InnerClass {public:TInner value;InnerClass(TInner val) : value(val) {}void print() {std::cout << "Inner value: " << value << std::endl;}};
};


这里,InnerClass 是在 OuterClass 类模板内部定义的成员类模板。

类外定义

如果成员模板的实现较为复杂,或者为了保持类定义的简洁性,可以将其定义在类外部。

与类模板的普通函数成员不同,成员模板是函数模板。当我们在类模板外定义一个成674 员模板时,必须同时为类模板和成员模板提供模板参数列表。类模板的参数列表在前,后
跟成员自己的模板参数列表:

成员函数模板的类外定义
template<typename TClass>
class MyClass {
public:// 成员函数模板的声明template<typename T>void printValue(T value);
};// 成员函数模板的类外定义
template<typename TClass>
template<typename T>
void MyClass<TClass>::printValue(T value) {std::cout << "Value of type " << typeid(T).name() << ": " << value << std::endl;
}


在这个例子中,printValue 成员函数模板首先在类模板 MyClass 内部声明,然后在类外部进行了定义。注意类外定义时的语法:需要两层 template 关键字,第一层用于类模板参数,第二层用于成员函数模板参数。

成员类模板通常不采用类外定义,因为它们往往与外围类紧密相关,并且它们的定义不太可能非常长或复杂到需要移出类定义之外。然而,如果需要,理论上也是可以在类外定义的,但这样做会使代码变得难以阅读和维护。

注意事项

  • 无论是类内定义还是类外定义,成员模板都是在编译时展开的,编译器会为每种不同的类型生成相应的实例。
  • 类外定义成员模板时,需要确保模板的定义在使用它的每个编译单元中都是可见的,这通常意味着将定义放在头文件中。
  • 类模板的成员模板增加了代码的复杂性,应谨慎使用,并确保其带来的灵活性确实是项目所需的。

控制实例化

当模板被使用时才会进行实例化这一特性意味着,相同他实例可能出现在多个对象文件中。

当两个或多个独立编译的源文件使用了相同的模板,并提供了相同的模板参数时,每个文件中就都会有该模板的一个实例。

在大系统中,在多个文件中实例化相同模板的额外开销可能非常严重。在新标准中,我们可以通过显式实例化来避免这种开销。

一个显式实例化有如下形式:

extern template declaration; // 实例化声明
template declaration; // 实例化定义

declaration是一个类或函数声明,其中所有模板参数已被管换为模板实参。例如./7实例化声明与定义

extern template class Blob<string>; // 声明
template int compare(conat int&, const int&);// 定义

当编译器遇到extern模板声明时,它不会在本文件中生成实例化代码。

将一个实例化声明为extern 就表示承诺在程序其他位置有该实例化的一个非extern于一个给定的实例化版本,可能有多个extern声明,但必须只有一个定义。

由于编译器在使用一个模板时自动对其实例化,因此extern声明必须出现在任何使用此实例化版本的代码之前:

// Application.cc
//这些模板类型必须在程序其他位置进行实例化
extern template class Blob<string>;
extern template int compare(const int&, const int&);
Blob<string> sal,sa2;// 实例化会出现在其他位置
// Blob<int>及其接受initializer list的构造函数在本文件中实例化
Blob<int> al = {0,1,2,3,4,5,6,7,8,9};
Blob<int> a2(a1);//拷贝构造函数在本文件中实例化
int i = compare(al[0],a2[0]);//实例化出现在其他位置

文件Application.o将包含Blob<int>的实例及其接受initializer_list参数的构造函数和拷贝构造函数的实例。

而compare<int>函数和Blob<string>类将不在本文件中进行实例化。这些模板的定义必须出现在程序的其他文件中:

// templateBuild.cc
// 实例化文件必须为每个在其他文件中声明为extern的类型和函数提供一个(非extern)
//的定义
template int compare(const int, const int&);
template class Blob<string>;// 实例化类模板的所有成员

当编译器遇到一个实例化定义(与声明相对)时,它为其生成代码。

因此,文件templateBuild.o将会包含compare的int实例化版本的定义和Blob<string>类的定义。当我们编译此应用程序时,必须将templateBuild.o和Application.o链接到一起。

对每个实例化声明,在程序中某个位置必须有其显式的实例化定义。

实例化定义会实例化所有成员

一个类模板的实例化定义会实例化该模板的所有成员,包括内联的成员函数。

当编译器遇到一个实例化定义时,它不了解程序使用哪些成员函数。

因此,与处理类模板的普通实例化不同,编译器会实例化该类的所有成员。即使我们不使用某个成员,它也会被实例化。

因此,我们用来显式实例化一个类模板的类型, 必须能用于模板的所有成员。

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

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

相关文章

WebAuthn:更好地保护线上敏感信息

1. 引言 2023年知乎博客 WebAuthn: 真正的无密码身份认证 总结得很赞。 在数字时代&#xff0c;密码已成为人们日常生活和在线活动中不可或缺的一部分。尽管互联网已经发展了 20 多年&#xff0c;许多方面都有了巨大的改进&#xff0c;但只有密码&#xff0c;还是 20 年前的用…

Python球球大作战

文章目录 写在前面球球大作战程序设计注意事项写在后面 写在前面 安装pygame的命令&#xff1a; pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pygame球球大作战 《球球大作战》是一款简单易上手、充满趣味性和竞技性的休闲手游。游戏的核心玩法可以用一句话概…

python数据分析和可视化【4】星巴克数据分析

有一组关于全球星巴克门店的统计数据directory.csv&#xff0c;分析了在不同国家和地区以及中国不同城市的星巴克门店的数量。 要求&#xff1a; &#xff08;1&#xff09;查看星巴克旗下有哪些品牌。如果我们只关心星巴克咖啡门店&#xff0c;则只需获取星巴克中Brand的数据集…

JUC:SimpleDateFormat的线程安全问题 以及 不可变类型DateTimeFormatter的使用

文章目录 不可变类SimpleDateFormat为什么不安全&#xff1f;解决 不可变类保证线程安全的实现 不可变类 SimpleDateFormat public static void main(String[] args) {SimpleDateFormat simpleDateFormat new SimpleDateFormat("yyyy-MM-dd");for (int i 0; i <…

二维码的生成、下载Java,并返回给前端展示

分析 将生成的二维码图片&#xff0c;以IO流的方式&#xff0c;通过response响应体直接返回给请求方。 第一、不需要落到我们的磁盘&#xff0c;操作在内存中完成&#xff0c;效率比较高。 第二、所有生成二维码的请求&#xff0c;都可以访问这里&#xff0c;前端直接拿img标…

前端学习<四>JavaScript基础——04-标识符、关键字、保留字

变量的命名规则&#xff08;重要&#xff09; JS是大小敏感的语言。也就是说 A 和 a 是两个变量。大写字母是可以使用的&#xff0c;比如&#xff1a; var A 250; //变量1var a 888; //变量2 我们来整理一下变量的命名规则&#xff0c;非常重要。 必须遵守&#xff1a; 只…

使用阿里云试用Elasticsearch学习:1.3 基础入门——搜索-最基本的工具

现在&#xff0c;我们已经学会了如何使用 Elasticsearch 作为一个简单的 NoSQL 风格的分布式文档存储系统。我们可以将一个 JSON 文档扔到 Elasticsearch 里&#xff0c;然后根据 ID 检索。但 Elasticsearch 真正强大之处在于可以从无规律的数据中找出有意义的信息——从“大数…

Liunx进程信号

进程信号 进程信号什么是信号liunx信号种类 前后台进程前后台进程的概念 进程信号的产生键盘产生 阻塞信号信号的捕捉用户态和内核态 信号的捕捉函数 进程信号 什么是信号 信号是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知…

【leetcode面试经典150题】5.多数元素(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

【MySQL】:深入解析多表查询(上)

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. 多表关系1.1 一对多1.2 多对多1.3 一对一 二. 多表查询概述2.1 概述2.2 分类…

代码随想录Day45

Day 45 动态规划 part07 今日任务 爬楼梯 &#xff08;进阶&#xff09; 零钱兑换 279.完全平方数 代码实现 爬楼梯 &#xff08;进阶&#xff09; 完全背包应用&#xff0c;关键在于if判断 public static int climbStairs(int m, int n) {//如果每次可以爬m阶&#xff0c;…

基于Springboot的航班进出港管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的航班进出港管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结…

第十题:金币

题目描述 国王将金币作为工资&#xff0c;发放给忠诚的骑士。第一天&#xff0c;骑士收到一枚金币&#xff1b;之后两天&#xff08;第二天和第三天&#xff09;&#xff0c;每天收到两枚金币&#xff1b;之后三天&#xff08;第四、五、六天&#xff09;&#xff0c;每天收到…

中拔出溜的公司如何落地监控体系

又一项看似技术需求驱动&#xff0c;最终发现还是业务需求驱动的体系化建设。 0. 目录结构 1. 中拔出溜公司的特点2. 达成共识3. 推荐落地路线3.1 理论解析3.2 Loki Promtail Grafana 轻量级零侵入方案3.3 接入traceId3.4 基础设施监控 后记相关 1. 中拔出溜公司的特点 在传…

力扣---删除链表的倒数第 N 个结点

给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&#xff1a;[]示例 3&#xff1a…

解决Word文档中插入MathTypeca公式编号问题(适用于本科、硕士、博士论文编写)

公式编号 这写论文过程中&#xff0c;我们常用到的就是根据章节号要求每写一个公式就自动编号&#xff0c;而不是(1)、&#xff08;2&#xff09;之类的。那么如下图这样的是怎么实现的呢&#xff1f; 1.开启Mathtype右编号 这样你才能有一个编号的格式 2.对公式进行格式化…

C++入门(以c为基础)——学习笔记2

1.引用 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空 间。在语法层面&#xff0c;我们认为它和它引用的变量共用同一块内存空间。 可以取多个别名&#xff0c;也可以给别名取别名。 b/c/d本质都是别名&#…

网络通信(二)

UDP服务器接收数据和发送数据 UDP协议时&#xff0c;不需要建立连接&#xff0c;只需要知道对方的IP地址和端口号&#xff0c;就可以直接发数据包。但是&#xff0c;能不能到达就不知道了。虽然用UDP传输数据不可靠&#xff0c;但它的优点是和TCP比&#xff0c;速度快&#xf…

C++的 stack和queue 的应用和实现【双端队列的理解和应用】

文章目录 stack的理解和应用栈的理解栈的模拟实现string实现stackvector实现stack queue的理解和应用队列的理解队列的模拟实现 双端队列原理的简单理解deque的缺陷为什么选择deque作为stack和queue的底层默认容器STL标准库中对于stack和queue的模拟实现stack的模拟实现queue的…

【LangChain学习之旅】—(19)CAMEL:通过角色扮演进行思考创作内容

【LangChain学习之旅】—(19)CAMEL:通过角色扮演进行思考创作内容 CAMEL 交流式代理框架股票交易场景设计场景和角色设置提示模板设计定义CAMELAgent类,用于管理与语言模型的交互预设角色和任务提示任务指定代理系统消息模板创建 Agent 实例头脑风暴开始总结大模型的成功,…