C++11:如何编写一个类似于 get<tuple>... 但接收参数包的模板函数?
C++11: how to write a template function that works like get<tuple>... but receives param pack?
我知道在 c++11 中,get<> 模板函数与 std::tuple 一起使用来获取元组的索引值,这是在编译时解决的。
但我的要求是有一个名为get<>的模板函数,但接收一个参数包,所以代码应该如下所示:
#include<iostream>
using namespace std;
template<typename Head, typename ... Tail>
auto get(size_t index, Head&& t, Tail&&...tail){
return get(index-1, tail...);
}
template<typename Head, typename ... Tail>
Head get<0>(Head&& t, Tail&&...tail){
return t;
}
int main(){
cout<<get(3,"abc",'x',27,"hello")<<endl;
cout<<get(2,"abc",'x',28,"hello")<<endl;
return 0;
}
当然,它无法编译,因为我不知道如何编写这样的 "get" 模板函数。我希望主要功能将 运行 并打印如下:
hello
28
所以我的问题是:如何实现我上面提到的 "get" 模板?
谢谢!
您真的需要能够在运行时select索引吗?如果没有,以下应该足够了:
template <std::size_t Index, typename ...P> auto get(P &&... params)
{
return std::get<Index>(std::make_tuple(params...));
}
// ...
std::cout << get<1>("abc", 42, 123.456); // Prints 42
如果您确实希望将索引作为普通(运行时)参数,则必须 return 一个 std::variant<P...>
(或 std::any
,或标记联合) ,因为 return 类型不能依赖于运行时参数。
一个可能的实现可能是这样的:
template <typename ...P> std::variant<P...> get(std::size_t index, P &&... params)
{
std::size_t pos = 0;
std::variant<P...> ret;
((index == pos++ ? void(ret = params) : void()) , ...);
return ret;
}
// ...
std::cout << std::get<int>( get(1, "abc", 42, 123.456) ); // Prints 42 too, but looks ugly
或者您可以指定 return 类型作为模板参数:
template <typename T, typename ...P> T get(std::size_t index, P &&... params)
{
std::size_t pos = 0;
std::variant<P...> va;
((index == pos++ ? void(va = params) : void()) , ...);
if (T *ptr = std::get_if<T>(va))
return T;
else
throw /*something*/;
}
// ...
std::cout << get<int>(1, "abc", 42, 123.456); // 42
或者您可以将值传递给 function/functor:(感谢@Yakk)
template <typename T, typename ...P> void get(std::size_t index, T &&func, P &&... params)
{
std::size_t pos = 0;
((index == pos++ ? void(func(params)) : void()) , ...);
}
// ...
get(1, [](int x){std::cout << x;}, "abc", 42, 123.456); // 42
// Alternative:
// get(1, [](auto x){std::cout << x;}, "abc", 42, 123.456); // 42 too,
// but you could print any type which can be printed without having to
// specify the type manually.
我知道在 c++11 中,get<> 模板函数与 std::tuple 一起使用来获取元组的索引值,这是在编译时解决的。
但我的要求是有一个名为get<>的模板函数,但接收一个参数包,所以代码应该如下所示:
#include<iostream>
using namespace std;
template<typename Head, typename ... Tail>
auto get(size_t index, Head&& t, Tail&&...tail){
return get(index-1, tail...);
}
template<typename Head, typename ... Tail>
Head get<0>(Head&& t, Tail&&...tail){
return t;
}
int main(){
cout<<get(3,"abc",'x',27,"hello")<<endl;
cout<<get(2,"abc",'x',28,"hello")<<endl;
return 0;
}
当然,它无法编译,因为我不知道如何编写这样的 "get" 模板函数。我希望主要功能将 运行 并打印如下:
hello
28
所以我的问题是:如何实现我上面提到的 "get" 模板? 谢谢!
您真的需要能够在运行时select索引吗?如果没有,以下应该足够了:
template <std::size_t Index, typename ...P> auto get(P &&... params)
{
return std::get<Index>(std::make_tuple(params...));
}
// ...
std::cout << get<1>("abc", 42, 123.456); // Prints 42
如果您确实希望将索引作为普通(运行时)参数,则必须 return 一个 std::variant<P...>
(或 std::any
,或标记联合) ,因为 return 类型不能依赖于运行时参数。
一个可能的实现可能是这样的:
template <typename ...P> std::variant<P...> get(std::size_t index, P &&... params)
{
std::size_t pos = 0;
std::variant<P...> ret;
((index == pos++ ? void(ret = params) : void()) , ...);
return ret;
}
// ...
std::cout << std::get<int>( get(1, "abc", 42, 123.456) ); // Prints 42 too, but looks ugly
或者您可以指定 return 类型作为模板参数:
template <typename T, typename ...P> T get(std::size_t index, P &&... params)
{
std::size_t pos = 0;
std::variant<P...> va;
((index == pos++ ? void(va = params) : void()) , ...);
if (T *ptr = std::get_if<T>(va))
return T;
else
throw /*something*/;
}
// ...
std::cout << get<int>(1, "abc", 42, 123.456); // 42
或者您可以将值传递给 function/functor:(感谢@Yakk)
template <typename T, typename ...P> void get(std::size_t index, T &&func, P &&... params)
{
std::size_t pos = 0;
((index == pos++ ? void(func(params)) : void()) , ...);
}
// ...
get(1, [](int x){std::cout << x;}, "abc", 42, 123.456); // 42
// Alternative:
// get(1, [](auto x){std::cout << x;}, "abc", 42, 123.456); // 42 too,
// but you could print any type which can be printed without having to
// specify the type manually.