您的位置:首页 > 编程语言 > C语言/C++

c++11 之可变参数模板

2016-07-28 14:16 387 查看
目的:包含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);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++11 模板 可变参数