c++11 之可變引數模板
目的:包含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);