1. 程式人生 > >c++11 之可變引數模板

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);