文章目录
可变参语法
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::forward
void 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: 10
forwardArgs(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;
}