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