1、可变长参数模板
-
可变长参数是C++类模板编程中非常重要的一个东西,也是C++11引入的新特性,通过使用…来表示参数长度不固定
-
可以通过sizeof…(args)获取传入参数的个数数量
-
&&表示万能引用,需要注意的是如果不使用万能引用,传入参数的类型需要注意是左值还是右值,要与模板的参数类型匹配。
// 1. 类模板
template<class... T>
class A {
public:A(const T&... args){}
};// 2. 函数模板
template<class...Args>
int func1(Args&&...args)
{return sizeof...(args);
}// 3. 可变参数构造tuple
template<class...Args>
auto return_tuple(Args&&... args)
{return std::make_tuple(args...);
}// 4. 可变参数构造array
template<class...Args>
auto return_array(const Args&... args)
{return std::array{args...};
}void test()
{A<int, std::string, int, bool> a(1, "abc", 4, false);std::cout << "参数个数: " << func1("1", 2, a, 'c', 1.5f) << std::endl; // 输出5auto t = return_tuple("1", 2, false, 3.14);std::cout << std::get<3>(t) << std::endl; // 输出3.14for(auto& x: return_array(1, 3, 4, 6)){ // 输出1 3 4 6std::cout << x << " ";}std::cout << std::endl;
}
2、可变长参数与完美转发
- 首先定义了一个类写出构造、拷贝构造和移动构造以及析构函数
- 然后写一个func函数,其中接收三个参数
- 最后写一个模板函数,对收到的函数和参数进行完美转发后绑定,最后执行绑定后的函数。
class Test{
public:int* m;Test() {m = new int(5);std::cout << "Test()" << std::endl;}Test(const Test& t){m = new int(*t.m);std::cout << "Test(const Test& t)" << std::endl;}Test(Test&& t) noexcept {m = t.m;t.m = nullptr;std::cout << "Test(Test&& t)" << std::endl;}virtual ~Test(){std::cout << "~Test()" << std::endl;}
};void func(int& a, int b, Test& test)
{std::cout << "func::a = " << a << std::endl;a = 123;b = 456;
}
template<class Func, class...Args>
void test(Func&& func, Args&&... args)
{auto f = std::bind(std::forward<Func>(func), std::forward<Args>(args)...);f();
}void test_func()
{{int a = 5;Test t;test(func, std::ref(a), 5, std::move(t));std::cout << "main::a = " << a << std::endl;}std::cout << __func__ << std::endl;
}/*
Test()
Test(Test&& t)
func::a = 5
~Test()
main::a = 123
~Test()
test_func
*/
3、折叠表达式
void print1(){std::cout << ", 函数递归调用出口";
}
template<class T, class...Args>
void print1(const T& t, Args&&... args)
{std::cout << t << " ";print1(std::forward<Args>(args)...);
}void test_expr()
{print("你好", 3.14159, 123, 'c', 111);
}
/*
你好 3.14159 123 c 111 , 函数递归调用出口
*/
C++11之后写这种模板函数的递归需要写一个空参函数和const T&t
参数,而在C++17之后可以折叠表达式展开。
// 折叠表达式
template<class...Args>
void print2(Args&&... args)
{((std::cout << args << " "), ...);
}//或者下面这种写法,都是C++17提供的
template<class T, class...Args>
void print3(const T& t, Args&&... args)
{std::cout << t << " ";if constexpr (sizeof...(args)){print3(args...);}
}