可变参数包的个数可以为0,也可以为N
template<class ...Args>
返回类型 函数名(Args ...args)
{
//函数体
}
如:
template<class ...Args>
void ShowList(Args... args)
{}
int main()
{
ShowList();
ShowList(1);
ShowList(1,2);
ShowList(1,2,"abc");
return 0;
}
在可变参数模板中,我们可以传入任意类型
template<class ...Args>
void ShowList(Args ...args)
{
cout << sizeof...(args) << endl;//注意...的位置
}
int main()
{
ShowList();
ShowList(1);
ShowList(1,2);
ShowList(1,2,"abc");
return 0;
}
使用递归方式展开参数包我们可以:
综合以上的考虑方向,我们首先考虑到的就是当传入0个参数时怎么办?
这时需要我们设置一个专门接受0个参数的函数,来起到终止的作用
void ShowList()
{
cout << "起到了终止的作用" << endl;
}
template<class T, class ...Args >
void ShowList(T value,Args ...args)
{
cout << value << endl;
ShowList(args...);
}
int main()
{
ShowList();
ShowList(1);
ShowList(1,2);
ShowList(1,2,"abc");
return 0;
}
如果想要无论外部调用时传入多少个参数,最终匹配到的都是同一个函数了时,我们需要这样修改
//递归终止函数
void ShowListArg()
{
cout << endl;
}
//展开函数
template<class T, class ...Args>
void ShowListArg(T value, Args... args)
{
cout << value << " "; //打印传入的若干参数中的第一个参数
ShowListArg(args...); //将剩下参数继续向下传
}
//供外部调用的函数
template<class ...Args>
void ShowList(Args... args)
{
ShowListArg(args...);
}
也可以编写带参的退出函数,这样一来吗,当参数包剩下一个参数的时候,就会自动匹配退出函数
//递归终止函数
template<class T>
void ShowListArg(const T& t)
{
cout << t << endl;
}
//展开函数
template<class T, class ...Args>
void ShowListArg(T value, Args... args)
{
cout << value << " "; //打印传入的若干参数中的第一个参数
ShowList(args...); //将剩下参数继续向下传
}
//供外部调用的函数
template<class ...Args>
void ShowList(Args... args)
{
ShowListArg(args...);
}
但该方法有一个弊端就是,我们在调用ShowList函数时必须至少传入一个参数,否则就会报错。因为此时无论是调用递归终止函数还是展开函数,都需要至少传入一个参数。
我们可以用一个数组来接收参数包。例如以下方式:
int a[] = {1,2,3};//列表初始化方式
我们可以通过调用参数包,将参数包里的内容通过列表初始化的方式放入数组中。如下
但是以上代码有一个问题,我们传入的参数必须为整型才配用这个逻辑,怎么解决这个问题呢?
逗号表达式解决
这样一来,在执行逗号表达式时就会先调用处理函数处理对应的参数,然后再将逗号表达式中的最后一个整型值作为返回值来初始化整型数组。比如
实际上我们也可以不用逗号表达式,因为这里的问题就是初始化整型数组时必须用整数,那我们可以将处理函数的返回值设置为整型,然后用这个返回值去初始化整型数组也是可以的。比如:
//支持无参调用
void ShowList()
{
cout << endl;
}
//处理函数
template<class T>
int PrintArg(const T& t)
{
cout << t << " ";
return 0;
}
//展开函数
template<class ...Args>
void ShowList(Args... args)
{
int arr[] = { PrintArg(args)... }; //列表初始化
cout << endl;
}
C++11标准给STL中的容器增加emplace版本的插入接口,比如list容器的push_front、push_back和insert函数,都增加了对应的emplace_front、emplace_back和emplace函数。如下:
emplace:接口的意义:
例如:
namespace test
{
class string
{
public:
string(const char* str)
:_size(strlen(str))
, _capacity(_size)
,_str(new char[_size + 1])
{
strcpy(_str, str);
cout << "string(const char* str) -- 构造函数" << endl;
}
//交换两个对象的数据
void swap(string& s)
{
//调用库里的swap
::swap(_str, s._str); //交换两个对象的C字符串
::swap(_size, s._size); //交换两个对象的大小
::swap(_capacity, s._capacity); //交换两个对象的容量
}
//拷贝构造函数(现代写法)
string(const string& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
cout << "string(const string& s) -- 拷贝构造" << endl;
string tmp(s._str); //调用构造函数,构造出一个C字符串为s._str的对象
swap(tmp); //交换这两个对象
}
//移动构造
string(string&& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
cout << "string(const string& s) -- 移动构造" << endl;
swap(s); //交换这两个对象
}
//拷贝赋值函数(现代写法)
string& operator=(const string& s)
{
cout << "string& operator=(const string& s) -- 深拷贝" << endl;
string tmp(s); //用s拷贝构造出对象tmp
swap(tmp); //交换这两个对象
return *this; //返回左值(支持连续赋值)
}
//移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动赋值" << endl;
swap(s);
return *this;
}
//析构函数
~string()
{
//delete[] _str; //释放_str指向的空间
_str = nullptr; //及时置空,防止非法访问
_size = 0; //大小置0
_capacity = 0; //容量置0
}
private:
size_t _size;
size_t _capacity;
char* _str;
};
}
int main()
{
list<pair<int, test::string>> mylist;
pair<int, test::string> kv(1, "one");
mylist.emplace_back(kv);
cout << endl;
mylist.emplace_back(pair<int, test::string>(2, "two"));
cout << endl;
mylist.emplace_back(2, "two");
cout << endl;
}
int main()
{
list<pair<int, test::string>> mylist;
pair<int, test::string> kv(1, "one");
mylist.push_back(kv);
cout << endl;
mylist.push_back(pair<int, test::string>(2, "two"));
cout << endl;
mylist.push_back({ 2, "two" });
cout << endl;
}
两者最明显的区别就是一个支持参数包传,一个支持列表初始化传,但是站在内存角度来说emplace跟省空间,emplace可以无脑使用
总结一下: