I know that in c++11, the get<> template function works with std::tuple to get the indexed value of the tuple, this is resolved at compile time.
But my requirement is to have a template function called get<> but receives a parameter pack, so the code should be like below:
#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;
}
Well it doesn't compile, for sure, as I don't know how to write such a "get" template function. I wish the main function will run and print as below:
hello
28
So my question: how to implement this "get" template as I mentioned above? Thanks!
Do you really need to be able to select the index at runtime? If not, following should be enough:
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
If you indeed want to have the index as a normal (runtime) parameter, you'd have to return a std::variant<P...>
(or std::any
, or a tagged union), since a return type can't depend on a runtime parameter.
A possible implementation could look like this:
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
Or you could specify return type as a template parameter:
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
Or you could pass the value to a function/functor instead: (thanks @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.