C++11:可变模板参数、emplace
1、可变模板参数的概念
C++11支持可变参数模板,也就是说支持可变数量参数的函数模板和类模板,例如:
cpp
template <class ...Args> void Func(Args... args) {}
template <class ...Args> void Func(Args&... args) {}
template <class ...Args> void Func(Args&&... args) {}
上面的参数中Args是一个模板参数包,args是一个函数形参参数包,声明一个参数包Args... args,这个参数包中可以包含0到任意个模板参数
可变参数模板就是支持任意数量、任意类型参数,参数包是用...来打包一堆参数
我们无法直接获取参数包args中的每个参数,语法不支持使用argsi这样的方式获取可变参数,只能通过展开参数包的方式来获取参数包中的每个参数
2、获取参数包中参数的个数
cpp
template <class ...Args>
void Print(Args&&... args)
{
cout << sizeof...(args) << endl;
}
int main()
{
double x = 2.2;
Print(); // 包里有0个参数
Print(1); // 包里有1个参数
Print(1, string("xxxxx")); // 包里有2个参数
Print(1.1, string("xxxxx"), x); // 包里有3个参数
return 0;
}
输出结果:
0
1
2
3
可以通过sizeof... (args)来查看参数包中的参数个数
3、递归函数方式展开参数包
cpp
// 递归终止函数
void ShowList()
{
cout << endl; //所有参数打印完,换行收尾
}
// 每次拆分出第一个参数T x,剩下的打包成Args... args
template <class T, class ...Args>
void ShowList(T x, Args... args)
{
// 先打印当前拆出来的第一个参数
cout << x << " ";
// 将剩下没打印的args包继续传给自己,递归拆解
ShowList(args...);
}
// 接收任意个任意类型参数包,转发给ShowList处理
template <class ...Args>
void Print(Args... args)
{
ShowList(args...);
}
int main()
{
double x = 2.2;
Print(); // 包里有0个参数
Print(1); // 包里有1个参数
Print(1, string("xxxxx")); // 包里有2个参数
Print(1.1, string("xxxxx"), x); // 包里有3个参数
return 0;
}
输出结果:
1
1 xxxxx
1.1 xxxxx 2.2
该方法是通过递归调用ShowList函数去获取参数包中的参数,每递归一次就可以从参数包中取出一个参数存到形参val中。当参数包把最后一个参数传递给ShowList(T x, Args... args) 后,参数包中没有任何东西,相当于无参调用ShowList() ,所以写了上面的递归终止函数,这样就可以把参数包中的最后一个参数提取出来,并结束递归
在编译器底层:

5、emplace系列接口
5.1 push_back的用法
下面使用模拟实现的list配合string演示push_back和emplace_back的用法与差异
cpp
int main()
{
MyList::list<MyList::string> lt;
// 传左值,跟push_back一样,走拷贝构造
MyList::string s1("111111111111");
lt.emplace_back(s1);
cout << "\n" << endl;
// 右值,跟push_back一样,走移动构造
lt.emplace_back(move(s1));
cout << "\n" << endl;
// 直接把构造string参数包往下传,直接用string参数包构造string
// 这里达到的效果是push_back做不到的
lt.emplace_back("111111111111"); // 构造
lt.emplace_back(10, 'x'); // 构造
lt.push_back("111111111111"); // 构造+移动构造
cout << "\n" << endl;
// 链表中存日期类对象
//lt.emplace_back(2025, 1, 1); // 构造
//lt.push_back({ 2025, 1, 1 }); // 构造+拷贝构造
MyList::list<pair<MyList::string, int>> lt1;
// 跟push_back一样
// 构造pair + 拷贝/移动构造pair到list的节点中data上
pair<MyList::string, int> kv("苹果", 1);
lt1.emplace_back(kv);
cout << "\n" << endl;
// 跟push_back一样
lt1.emplace_back(move(kv));
cout << "\n" << endl;
////////////////////////////////////////////////////////////////////
// 直接把构造pair参数包往下传,直接用pair参数包构造pair
// 这里达到的效果是push_back做不到的
lt1.emplace_back("苹果", 1);
// lt1.emplace_back({ "苹果", 1 }); 不支持,形参是模板,无法推导形参类型
lt1.push_back({ "苹果", 1 }); // 隐式类型转换传参
cout << "\n" << endl;
return 0;
}
输出结果:

由上面的代码可以发现,如果使用push_back传递构造pair的参数,那么会产生构造和移动构造,如果使用emplace_back传递构造pair的参数,那么只会产生一次构造,可以提高效率
5.2 emplace_back使用方法
cpp
template <class... Args>
void emplace_back (Args&&... args);
emplace_back和push_back最大的区别是push_back的参数类型是固定的,而emplace_back采用的是可变模板参数,push_back的三种使用方式同样适用于emplace_back
list中的应用
cpp
template <class... Args>
void emplace_back(Args&&... args)
{
emplace(end(), forward<Args>(args)...);
}
// 优化后的insert
template <class... Args>
void emplace(iterator pos, Args&&... args)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(forward<Args>(args)...);
// prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
}
// 改造构造函数
template <class... Args>
list_node(Args&&... args)
: _next(nullptr)
, _prev(nullptr)
, _data(forward<Args>(args)...)
{
}
普通insert,必须先造好完整pair对象再插入:
cpp
bit::list<pair<string, int>> lt;
lt.insert(lt.begin(), pair<string,int>("苹果",1));
emplace函数:
cpp
lt.emplace(it, "苹果", 1);
emplace_back新增的用法:
cpp
lt.emplace_back(10, 'x');
使用函数:
cpp
template <class... Args>
void emplace_back(Args&&... args)
{
emplace(end(), forward<Args>(args)...);
}
emplace_back 直接传构造参数,原地构造,无临时
push_back只能传完整对象,必须先生成临时string
cpp
lt.push_back(string(10, 'x'));
6、list改良后代码
cpp
#pragma once
#pragma once
#include<assert.h>
namespace MyList
{
template<class T>
struct list_node
{
list_node* _next;
list_node* _prev;
T _data;
list_node(const T& x)
:_next(nullptr)
, _prev(nullptr)
, _data(x)
{
}
list_node(T&& x = T())
:_next(nullptr)
, _prev(nullptr)
, _data(move(x))
{
}
template <class... Args>
list_node(Args&&... args)
: _next(nullptr)
, _prev(nullptr)
, _data(forward<Args>(args)...)
{
}
};
// typedef list_iterator<T, T&, T*> iterator;
// typedef list_iterator<T, const T&, const T*> const_iterator;
template<class T, class Ref, class Ptr>
struct list_iterator
{
typedef list_node<T> Node;
typedef list_iterator<T, Ref, Ptr> Self;
Node* _node;
list_iterator(Node* node)
:_node(node)
{
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
Self operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
template<class T>
class list
{
typedef list_node<T> Node;
public:
typedef list_iterator<T, T&, T*> iterator;
//typedef list_const_iterator<T> const_iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
list()
{
empty_init();
}
list(initializer_list<T> il)
{
empty_init();
for (auto& e : il)
{
push_back(e);
}
}
// lt2(lt1)
//list(const list<T>& lt)
list(list<T>& lt)
{
empty_init();
for (auto& e : lt)
{
push_back(e);
}
}
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
// lt1 = lt3
list<T>& operator=(list<T> lt)
{
swap(lt);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
auto it = begin();
while (it != end())
{
it = erase(it);
}
}
size_t size()
{
return _size;
}
void push_back(const T& x)
{
insert(end(), x);
}
// 不是万能引用
// T是通过实参传递推导的吗?不是,T是list实例化的
void push_back(T&& x)
{
//insert(end(), move(x));
insert(end(), (T&&)x);
}
// 万能引用
//template<class X>
//void push_back(X&& x)
//{
// // 保持他的属性,往下传递,左值引用就是保持左值属性,右值引用就保持右值属性
// insert(end(), forward<X>(x));
//}
//template<class X>
//void insert(iterator pos, X&& x)
//{
// Node* cur = pos._node;
// Node* prev = cur->_prev;
// Node* newnode = new Node(forward<X>(x));
// // prev newnode cur
// prev->_next = newnode;
// newnode->_prev = prev;
// newnode->_next = cur;
// cur->_prev = newnode;
// ++_size;
//}
template <class... Args>
void emplace_back(Args&&... args)
{
emplace(end(), forward<Args>(args)...);
}
template <class... Args>
void emplace(iterator pos, Args&&... args)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(forward<Args>(args)...);
// prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
void insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
// prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
}
void insert(iterator pos, T&& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(move(x));
// prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
}
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
--_size;
//return iterator(next);
return next;
}
private:
Node* _head;
size_t _size;
};