0

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!

Troskyvs
  • 7,537
  • 7
  • 47
  • 115
  • 1
    You can't, the return type of `get` is not fixed. Your return type can't depend on a runtime parameter. – François Andrieux Jul 25 '17 at 17:17
  • @FrançoisAndrieux I don't see the OP mentioning that anywhere though. Could you please point out where you see that? Well, I guess the code is a hint... – Rakete1111 Jul 25 '17 at 17:22
  • @FrançoisAndrieux Yeah, I wasn't very sure. But I've reopened it just in case. – Rakete1111 Jul 25 '17 at 17:27
  • You seem to want `index` as a runtime parameter in your sample code. That makes this problem almost impossible, and not possible with *exactly* your syntax. A description of what behavior you actually need (not want, what *exactly* do you **need**) would make your question answerable. – Yakk - Adam Nevraumont Jul 25 '17 at 18:00

1 Answers1

3

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.
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207