C++11 新特性:可变参数模板、类功能与 STL 变化
C++11 引入可变参数模板支持零或多个参数,通过折叠规则实例化。新增移动语义支持默认移动构造和赋值,配合 default 和 delete 控制函数生成。final 和 override 修饰符增强继承控制。STL 增加 unordered_map/set 等新容器及 emplace 接口优化对象构造,支持右值引用和范围遍历。

C++11 引入可变参数模板支持零或多个参数,通过折叠规则实例化。新增移动语义支持默认移动构造和赋值,配合 default 和 delete 控制函数生成。final 和 override 修饰符增强继承控制。STL 增加 unordered_map/set 等新容器及 emplace 接口优化对象构造,支持右值引用和范围遍历。

class ...Args,参数类型是 Args...template<class ...Args> void Func(Args... args) {}template<class ...Args> void Func(Args&... args) {}template<class ...Args> void Func(Args&&... args) {}class... 或 typedef... 指出接下来的参数表示零或多个类型列表;在函数参数列表中,类型名后面跟 ... 指出接下来表示零或多个形参对象列表;函数参数包可以用左值引用或右值引用表示。每个参数实例化时遵循折叠规则。sizeof... 运算符计算参数包中参数的个数(不是 sizeof,这是一个新的运算符)。template <class ...Args> void Print(Args&&... args) //0-N 个参数 { cout << sizeof...(args) << endl; }
int main() {
double x = 2.2;
Print(); // 包里有 0 个参数
Print(1); // 包里有 1 个参数
Print(1, string("xxx")); // 包里有 2 个参数
Print(1, string("xxx"), x); // 包里有 3 个参数
return 0;
}
// 原理 1:编译本质这里会结合引用折叠规则实例化出以下 4 个函数 // void Print(); // void Print(int&& arg1); // void Print(int&& arg1, string&& arg2); // void Print(double&& arg1, string&& arg2, double& arg3); //x 是左值,所以 double& // 原理 2:更本质去看有没有可变参数模板,我们是先出这样的多个函数模板才能支持这里的功能,有了可变参数模板,我们进一步被解放,他是类型泛化基础上叠加数量变化,让我们泛型编程更灵活 // template void Print(T1&& arg1); // template <class T1, class T2> void Print(T1&& arg1, T2&& arg2); // template <class T1, class T2, class T3> void Print(T1&& arg1, T2&& arg2, T3&& arg3);
for(size_t i = 0; i < sizeof...(args); i++) 是运行时获取和编译,所以不支持这样使用。表达式 (参数包)...,... 必须写在整个表达式的末尾,用来触发'对每个参数都执行一次表达式'的展开逻辑。例如:GetArg(args)... 的作用是:把参数包里的每个元素,分别传给 GetArg 函数,然后把所有 GetArg 的返回值,组成一个新的参数列表。void ShowList() { //编译器时递归的终止条件,参数包是 0 个时,直接匹配这个函数
cout << endl;
}
template<class T,class ...Args> void ShowList(T x, Args... args) {
cout << x << " ";
//args 是 N 个参数的参数包,调用 ShowList,参数包的第一个传给 x,剩下 N-1 个传给第二个参数包
ShowList(args...);
}
template<class ...Args> void Print(Args... args) {
ShowList(args...);
}
int main() {
Print();
Print(1);
Print(1, string("xxx"));
Print(1, string("xxx"), 2.2);
return 0;
}
//Print(1, string("xxxxx"), 2.2);调用时
//本质编译器将可变参数模板通过模式的包扩展,
//编译器推导的以下三个重载函数函数
// void ShowList(double z) { cout << z << " "; ShowList(); }
// void ShowList(string y, double z) { cout << y << " "; ShowList(z); }
// void ShowList(int x, string y, double z) { cout << x << " "; ShowList(y, z); }
// void Print(int x, string y, double z) { ShowList(x, y, z); }

template <class T> const T& GetArg(const T& x) { cout << x << " "; return x; }
template <class ...Args> void Arguments(Args... args) { }
template <class ...Args> void Print(Args... args) {
Arguments(GetArg(args)...); //GetArg(args)...表示要展开,传入三个参数
//实例化出 Arguments(GetArg(x), GetArg(y), GetArg(z));
//编译时展开为:Arguments(GetArg(1), GetArg(string("xxxxx")), GetArg(2.2));
}
int main() {
Print(1, string("xxxxx"), 2.2);
return 0;
}
emplace_back 和 push_back 是一样的;部分场景下,emplace 可以直接构造,push 和 insert 是 构造 + 移动构造 或 构造 + 拷贝构造。int main() {
list<std::string> lt;
//传左值,跟 push_back 一样,走拷贝构造
std::string s1("111111");
//string(char* str) -- 构造
lt.emplace_back(s1);
//string(const string& s) -- 拷贝构造
cout << "*********************************" << endl;
//右值,跟 push_back 一样,走移动构造
lt.emplace_back(move(s1));
//string(string&& s) -- 移动构造
cout << "*********************************" << endl;
//直接把构造 string 参数包往下传,直接用 string 参数包构造 string
//这里达到的效果是 push_back 做不到的
lt.emplace_back("11111");
//string(char* str) -- 构造
cout << "*********************************" << endl;
list<pair<std::string, int>> lt1;
//跟 push_back 一样
//构造 pair + 拷贝/移动构造 pair 到 list 的节点中 data 上
pair<std::string, int> kv("苹果", 1);
//string(char* str) -- 构造
lt1.emplace_back(kv);
//string(const string& s) -- 拷贝构造
cout << "*********************************" << endl;
//跟 push_back 一样
lt1.emplace_back(move(kv));
//string(string&& s) -- 移动构造
cout << "*********************************" << endl;
//直接把构造 pair 参数包往下传,直接用 pair 参数包构造 pair
//这里达到的效果是 push_back 做不到的
lt1.emplace_back("苹果", 1);
//string(char* str) -- 构造
cout << "*********************************" << endl;
return 0;
}
//构造函数
template <class ...Args> ListNode(Args&&... args) : _next(nullptr) , _prev(nullptr) , _data(std::forward<Args>(args)...) {}
//emplace_back 实现
template <class ...Args> void emplace_back(Args&&... args) {
insert(end(), std::forward<Args>(args)...);
}
template <class... Args> 或 template <typename... Args>// ... 跟在 Args 后面,声明 Args 是'类型参数包'(装 0~N 个类型)
template <class... Args> void Print(Args&&... args); // 这里的 Args 就是上面声明的类型包
void Func(T... args)(T 是类型,args 是值参数包)// 先声明类型包 Ts,再用 Ts...声明值参数包 args
template <typename... Ts> void Func(Ts... args) {
// args 是值参数包,装 0~N 个值
}
GetArg(args) 是'包含参数包的表达式',... 跟在这个表达式后面,才会对每个参数执行一次 GetArgtemplate <class... Args> void Print(Args&&... args) {
// 核心:GetArg(args)... 是'表达式 (参数包)...',...在末尾触发展开
Arguments(GetArg(args)...); // 展开逻辑(编译时):
// Print(1, "xxx", 2.2) → Arguments(GetArg(1), GetArg("xxx"), GetArg(2.2))
}
template <typename... Ts> void PrintSize(Ts... args) {
// 展开参数包,计算每个参数的大小,存入数组
int sizes[] = {sizeof...(args)...}; // 展开逻辑:
// PrintSize(1, "xxx", 2.2) → int sizes[] = {sizeof(1), sizeof("xxx"), sizeof(2.2)};
for (auto s : sizes) {
cout << s << " "; // 输出:4 4 8(不同编译器可能有差异)
}
}
// 递归终止函数(参数包为空时调用)
void RecursivePrint() {
cout << endl;
}
// 递归展开函数(拆出第一个参数,剩下的继续递归)
template <typename T, typename... Ts> void RecursivePrint(T first, Ts... rest) {
cout << first << " ";
RecursivePrint(rest...); // ...跟在 rest 后面,展开剩余参数包
}
// 调用:RecursivePrint(1, "xxx", 2.2);
// 执行流程:
// 1. RecursivePrint(1, "xxx", 2.2) → 输出 1,调用 RecursivePrint("xxx", 2.2)
// 2. RecursivePrint("xxx", 2.2) → 输出 xxx,调用 RecursivePrint(2.2)
// 3. RecursivePrint(2.2) → 输出 2.2,调用 RecursivePrint()
// 4. RecursivePrint() → 输出换行,结束
template <class... Args> void Print(Args&&... args) {
// Args&&... 是'万能引用的参数包'
// Args&& 是万能引用,... 是参数包展开,两者结合表示'0~N 个万能引用参数'
}
class Person {
public:
Person(const char*, int age = 0) :_name(name) ,_age(age) { }
private:
std::string _name;
int _age;
};
int main() {
Person s1;
//string(char* str) -- 构造
Person s2 = s1;
//string(const string& s) -- 拷贝构造
Person s3 = std::move(s1);
//string(string&& s) -- 移动构造
Person s4;
//string(char* str) -- 构造
s4 = std::move(s2);
//string& operator=(string&& s) -- 移动赋值
return 0;
}
=delete 即可,该语法指示编译器不生产对应函数的默认版本,称 =delete 修饰的函数为删除函数。class Person {
public:
Person(const char*, int age = 0) :_name(name) ,_age(age) { }
//Person(const Person& p) // :_name(p._name) // ,_age(p._age) //{ }
Person(Person&& p) = default; //强制生成移动构造
Person(const Person& p) = delete; //限制生成拷贝构造(禁止调用)
private:
std::string _name;
int _age;
};
int main() {
Person s1;
//Person s2 = s1; //不能使用拷贝构造
Person s3 = std::move(s1);
return 0;
}
class Car {
public:
virtual void Drive() final {}
};
class Benz :public Car {
public:
// error C3248: "Car::Drive": 声明为 "final" 的函数不能由 "Benz::Drive" 重写
virtual void Drive() { cout << "Benz 舒适" << endl; }
};
class Car {
public:
virtual void Drive() {}
};
class Benz :public Car {
public:
virtual void Drive() override { cout << "Benz 舒适" << endl; }
};

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online