施磊老师高级c++(七)

发布于:2025-03-26 ⋅ 阅读:(8) ⋅ 点赞:(0)

可变参语法

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 是用于在容器中原地构造元素的方法,相比 insertpush_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;
}