文章目录
- 可变参语法
- **1. 基本的可变参数模板**
- **2. 可变参数展开(递归)**--这个有点意思
- **3. `sizeof...` 获取参数个数**
- **4. 结合 `std::forward` 进行完美转发**
- **5. 结合 `std::initializer_list`(参数展开的一种方式)**
- C++11容器emplace方法原理剖析(push/insert)
- 基本概念---实际就是得益于完美转发!!
- 核心区别
- 代码示例-1 -- vector
- 代码示例-2 -- map键值对 -- 这个很方便
- **底层原理:**
可变参语法
1. 基本的可变参数模板
template <typename... Args>
void func(Args... args) {// args 是一个参数包
}
typename... Args
代表 类型参数包,可以接受多个类型。Args... args
代表 函数参数包,对应多个参数。
2. 可变参数展开(递归)–这个有点意思
#include <iostream>// 终止递归的函数
void print() {std::cout << "End\n";
}// 递归展开参数包
template <typename First, typename... Rest>
void print(First first, Rest... rest) {std::cout << first << " ";print(rest...); // 递归调用
}int main() {print(1, 2.5, "hello", 'A'); // 输出: 1 2.5 hello A End
}
- 终止递归 的
print()
解决参数包为空的情况。 print(first, rest...)
依次展开参数包,类似递归。
3. sizeof...
获取参数个数
template <typename... Args>
void countArgs(Args... args) {std::cout << "参数个数: " << sizeof...(args) << "\n";
}int main() {countArgs(1, 2, 3.14, "text"); // 输出: 参数个数: 4
}
4. 结合 std::forward
进行完美转发
#include <iostream>
#include <utility> // std::forwardvoid show(int& x) { std::cout << "Lvalue: " << x << "\n"; }
void show(int&& x) { std::cout << "Rvalue: " << x << "\n"; }template <typename... Args>
void forwardArgs(Args&&... args) {show(std::forward<Args>(args)...);
}int main() {int a = 10;forwardArgs(a); // Lvalue: 10forwardArgs(20); // Rvalue: 20
}
Args&&... args
使args
成为 万能引用,能够接受左值和右值。std::forward<Args>(args)...
保持原本的左值/右值特性,防止不必要的拷贝或移动。
5. 结合 std::initializer_list
(参数展开的一种方式)
#include <iostream>template <typename... Args>
void printAll(Args... args) {(std::cout << ... << args) << "\n"; // C++17 折叠表达式
}int main() {printAll(1, " hello ", 3.14); // 输出: 1 hello 3.14
}
(std::cout << ... << args)
是 C++17 折叠表达式,简化递归调用。- 等价于:
((std::cout << args), ...)
,即std::cout << arg1 << arg2 << arg3;
C++11容器emplace方法原理剖析(push/insert)
本文讲解C++11中的emplace
基本概念—实际就是得益于完美转发!!
在 C++ STL(标准模板库)中,emplace
是用于在容器中原地构造元素的方法,相比 insert
或 push_back
等方法,它可以减少不必要的对象拷贝或移动,提高性能。
核心区别
push/insert 与 emplace 直接传入对象(包括临时对象), 是没有区别的
传入 对象构造需要的 参数 , 就有区别了
emplace 不会 拷贝, push/insert 则需要拷贝
给emplace
传入Test
对象构造所需要的参数,会直接在容器底层构造,不会产生构造和析构临时对象的额外花销,效率大大提高。
代码示例-1 – vector
#include <iostream>
#include <memory>
#include <unordered_map>
#include <list>
using namespace std;class Test
{
public:Test(int) { cout << "Test(int)" << endl; }Test(int, int) { cout << "Test(int, int)" << endl; }~Test() { cout << "~Test()" << endl; }Test(const Test&) { cout << "Test(const Test&)" << endl; }Test(Test&&) { cout << "Test(Test&&)" << endl; }
};int main()
{Test t1(10);vector<Test> vec;vec.reserve(10);// Test(int)// 直接插入对象,两个是没有区别的cout << "=================" << endl;// 匹配的是带左值引用参数的拷贝构造函数vec.push_back(t1);vec.emplace_back(t1);//Test(const Test&)//Test(const Test&)cout << "=================" << endl;// 匹配的是带右值引用参数的拷贝构造函数vec.push_back(Test(20));vec.emplace_back(Test(20));/* Test(int)Test(Test&&)~Test()Test(int)Test(Test&&)~Test()*/cout << "=================" << endl;// 区别在这vec.push_back(20); /*Test(int)Test(Test&&)~Test()
*/vec.emplace_back(20); // Test(int)//vec.push_back(30, 40); // pushback不支持多参这样传入vec.push_back(Test(30,40));vec.emplace_back(30, 40);cout << "=================" << endl;return 0;
}
代码示例-2 – map键值对 – 这个很方便
#include <iostream>
#include <string>
#include <map>
using namespace std;int main()
{map<int, string> m;m.insert(make_pair(1, "hzh1" ));m.insert({ 2, "hzh2" });cout << "=================" << endl;m.emplace(3, "hzh3"); // 这个效率很高的return 0;
}
底层原理:
注意一下 传参时和使用时 的 可变参写法
push_back 仅使用右值引用, 使得更简便, 完美转发 很重要
#include <iostream>
#include <string>
#include <map>
using namespace std;class Test
{
public:Test(int) { cout << "Test(int)" << endl; }Test(int, int) { cout << "Test(int, int)" << endl; }~Test() { cout << "~Test()" << endl; }Test(const Test&) { cout << "Test(const Test&)" << endl; }Test(Test&&) { cout << "Test(Test&&)" << endl; }
};// 空间配置器
template<typename T>
class MyAllocator
{
public:// 内存开辟、释放T* allocate(size_t size) { return (T*)malloc(sizeof(T) * size); }void deallocate(void* p) { free(p); }// 对象构造、析构 引用折叠template<typename... Ty>void construct(T* p, Ty&&... args){// 本例中是Test对象,args会完美转发,选择调用Test对象中不同的构造方式new (p) T(std::forward<Ty>(args)...);}void destroy(T* p) { p->~T(); }
};template<typename T, typename Alloc = MyAllocator<T>>
class vector
{
public:vector() :_vec(nullptr), _size(0), _idx(0) {}// 预留内存空间void reserve(size_t size){_vec = _allocator.allocate(size);_size = size;}// push_back 右值引用版本, 引用折叠template<typename Ty>void push_back(Ty&& val){_allocator.construct(_vec + _idx, std::forward<Ty>(val));++_idx;}// emplace_back 引用折叠、模板参数包template<typename... Ty>void emplace_back(Ty&&... args){_allocator.construct(_vec + _idx, std::forward<Ty>(args)...);++_idx;}private:T* _vec;int _size;int _idx;Alloc _allocator;
};int main()
{Test t1(10);vector<Test> vec;vec.reserve(10);// 直接插入对象,两个是没有区别的cout << "=================" << endl;// 匹配的是带左值引用参数的拷贝构造函数vec.push_back(t1);vec.emplace_back(t1);cout << "=================" << endl;// 匹配的是带右值引用参数的拷贝构造函数vec.push_back(Test(20));vec.emplace_back(Test(20));cout << "=================" << endl;// 区别在这vec.push_back(20); vec.emplace_back(20); //vec.push_back(Test(30, 40));vec.emplace_back(30, 40);cout << "=================" << endl;return 0;
}