gogoWebsite

Variable parameter template for c++11

Updated to 9 hours ago

Purpose: Contains 0 to any template parameters

Declaring a variable parameter template requires an ellipsis "..." after the typename or class

1. Variable parameter template function

template<class...T>

void f(T...args)

{

 cout<<sizeof...(args)<<endl;

}


There are two ways to expand variable parameters:

1. Expand parameters through recursive template functions

2. Expand parameter packages through comma expressions and initialization lists

For 1, why choose this by using type_traits? Because it is difficult and you can't understand it? ? ? ? ?

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

}

What a thing! ! ! ! ! ! ! ! ! ! ! ! ! ! !

(2) Comma and initialization list Expand parameter list

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

Here is an initialization list to initialize a variable-length array, {(printarg(args),0)..} expands to ((printarg(arg1),0), ((printarg2),0)....

The purpose of an int array is to expand parameter packages in the process of array construction

Improved, use initializer_list to replace the original int arr[] array

template<class...Args>

void expand(Args...args)

{

 std::initializer_list<int>{(printarg(args),0)...};

}

Improve again, use lambda expression:

template<typename...Args>

void expand(Args...args)

{

 std::initializer_list<int>{([&]{cout<<args<<endl;}),0)...};

}

2. Variable parameter template class

1. Expand parameter packages in template recursion and specialization

2. Inheritance method

Ahhh, I don’t want to watch it anymore. It feels like a function, so I won’t watch it for the time being! ! !


3. Variable parameters eliminate duplicate codes

template<typename T, typename... Args>

T* Instance(Args...args)

{

  return new T(args...);

}

In the above, Args is a value copy, with performance losses, and can be eliminated through perfect forwarding

template<typename T, typename...Args>

T* Instance(Args&&...args)

{

return new T(std::forward<Args>(args)..)..); //It's good to forward this thing perfectly

}

3. Comprehensive application of variable parameter templates and type_traits

An optional object that has not been initialized is an invalid value.

This is supported only in C++14, and can be implemented manually here

#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(())

   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   // This bool symbol is overloaded, represented by: 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(())

{

  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. Lazy evaluation

Similar to the bridge mode, it is to create an object and call member functions to the real evaluation.

Here is another example, this example is really cool:

#include<>

template<typename T>

struct Lazy

{

    Lazy(){};

template<typename Func, typename...Args>

// Save the function

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)..); //This sentence calls the above save function

}

The above function is an important function, mainly to use Lazy more conveniently and call its constructor:

int Foo(int x)

{

 return x*2;

}

int y =4;

auto lazyer = lazy(Foo, y);

cout<<()<<endl;

std::function<T()> is used to save the input function. This definition has no parameters, because a function can be initialized through a lambda expression, and the lambda expression can capture parameters without defining the parameters of the function.

You can also change the N-in-part parameter function to std::function<T()> through bind binding

m_func = std::bind(f, std::forward<Args>(args)...);

If you have placeholders, you need to write parameters:

std::function<void(int)> f = std::bind(&HT, std::placeholders::_1);