目的:包含0到任意个模板参数
声明可变参数模板需要在typename或class 后面加上省略号“...”
一、可变参数模板函数
template<class...T>
void f(T...args)
{
cout<<sizeof...(args)<<endl;
}
可变参数展开有两种方式:
1.通过递归的模板函数来将参数展开
2.通过逗号表达式和初始化列表方式展开参数包
对于1介绍一种通过type_traits来展开并打印参数包,为什么选用这个,因为很难,你看不懂?????
template<std::size_t i = 0, typename Tuple>
typename std::enable_if<I == std::tuple_size<Tuple>::value>::type printtp(Tuple t)
{
}
template<std::size_t i = 0, typename Tuple>
typename std::enable_if<i < std::tuple_size<Tuple>::value>::type printtp(Tuple t)
{
std::cout<<std::get<i>(t)<<endl;
printtp<i+1>(t);
}
template<typename...Args>
void print(Args...args)
{
printtp(std::make_tuple(args...));
}
什么玩意!!!!!!!!!!!!!!!
(2)逗号和初始化列表展开参数列表
template<class T>
void peintarg(T t)
{
cout<< t<<endl;
}
template< class ...Args>
void expand(Args...args)
{
int arr[] = { (printage(args), 0)...};
}
expand(1,2,3,4);
这里是通过一个初始化列表来初始化一个变长数组,{(printarg(args),0)...}展开为((printarg(arg1),0)、((printarg2),0).....
int数组的目的就是为了在数组构造的过程展开参数包
改进,使用initializer_list来替代原来的int arr[]数组
template<class...Args>
void expand(Args...args)
{
std::initializer_list<int>{(printarg(args),0)...};
}
再改进,用lambda表达式:
template<typename...Args>
void expand(Args...args)
{
std::initializer_list<int>{([&]{cout<<args<<endl;}),0)...};
}
二、可变参数模板类
1.模板递归和特化方式展开参数包
2.继承方式
啊啊啊,不想看了,感觉和函数的没两样,暂时不看了吧!!!
三、可变参数消除重复代码
template<typename T, typename... Args>
T* Instance(Args...args)
{
return new T(args...);
}
在上面中,Args是值拷贝的,存在性能损耗,可以通过完美转发来消除损耗
template<typename T, typename...Args>
T* Instance(Args&&...args)
{
return new T(std::forward<Args>(args)...); //完美转发这个玩意好啊
}
三、可变参数模板和type_traits的综合应用
1.optional
未被初始化的optional对象是无效值。
这个是c++14才支持的,这里可以进行手动实现
#include<type_traits>
template<typename T>
class Optional
{
using data_t = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
public:
Optional() {};
Optional(const T& v)
{
Create(v);
}
Optional(const Optional& other)
{
if(other.IsInit())
Assign(other);
}
~Optional()
{
Destroy();
}
template<class...Args>
void Emplace(Args&& ...args)
{
Destroy();
Create(std::forward<Args>(args)...);
}
bool IsInit() const { return m_hasInit;}
explicit operator bool() const // 这个bool符号重载,使用:if(xxx)来表示
{
return IsInit();
}
T const& operator*() const
{
if(IsInit())
{
return *((T*)(&m_data));
}
throw std::logic_error("is not init");
}
private:
template<class ...Args>
void Create(Args&& ...args)
{
new (&m_data) T(std::forward<Args>(args)...);
m_hasInit = true;
}
void Destroy()
{
if(m_hasInit)
{
m_hasInit = false;
((T*)(&m_data))->~T();
}
}
void Assign(const Option& other)
{
if(other.IsInit())
{
Copy(other.m_data);
m_hasInit = true;
}
else
{
Destroy();
}
}
void Copy(const data_t& val)
{
Destroy();
new (&m_data) T(*((T*)(&val)));
}
private:
bool m_hasInit = false;
data_t m_data;
};
2.惰性求值
类似桥接模式,是在创建对象,调用成员函数才是真正的求值
下面再举个例子,这个例子真的很吊的:
#include<Optional.hpp>
template<typename T>
struct Lazy
{
Lazy(){};
template<typename Func, typename...Args>
// 保存函数
Lazy(Func& f, Args&&...args)
{
m_func = [&f, &args..]{return f(args...);};
}
T& Value()
{
if(!m_value.IsInit())
{
m_value = m_func();
}
return *m_value;
}
bool IsVauleCreated() const
{
return m_value.IsInit();
}
private:
std::function<T()> m_func;
Optinal<T> m_value;
};
template<class Func,typename...Args>
Lazy<typename std::result_of<Func(Args...)>::type> lazy(Func&& func, Args&&...args)
{
return Lazy<typename std::result_of<Func(Args...)>::type>(std::forward<Func>(fun), std::forward<Args>(args)...); //这句调用 的就是上面的保存函数
}
上面一个函数是重要的函数,主要是为了更加方便地使用Lazy,调用其构造函数:
int Foo(int x)
{
return x*2;
}
int y =4;
auto lazyer = lazy(Foo, y);
cout<<lazyer.Value()<<endl;
std::function<T()>用来保存输入的函数,这个定义是没有参数的,因为可以通过一个lambda表达式去初始化一个function,而lambda表达式可以捕获参数,无需定义function的参数
还可以通过bind绑定来将N元的入参函数变为std::function<T()>
m_func = std::bind(f, std::forward<Args>(args)...);
如果含有占位符,则需要写参数:
std::function<void(int)> f = std::bind(&HT, std::placeholders::_1);