Question

The following minimal example compiles with g++ -std=c++11 -Wall tuple.cpp -o tuple:

#include <tuple>
#include <iostream>

template<int i>
char get_elem_i(std::tuple<char, char> t)
{
    return std::get<i>(t);
}

int main()
{
    std::tuple<char, char> t('H','i');
    char c = get_elem_i<0>(t);
    std::cout << "The char is: " << c << std::endl;
}

Now, I do not want to use a template which specifies the index (the exact reason why: I have templates that are deduced automatically, and I do not want to need to specify them all). So my first try was:

char get_elem_i(int i, std::tuple<char, char> t)
{
    return std::get<i>(t);
}

I understand that this can not compile. Is there any way to assure the compiler that i will be known at compile time? Maybe something like this?

char get_elem_i(compile_time_known int i, std::tuple<char, char> t)
Was it helpful?

Solution

You could use a std::array instead of a std::tuple. In the example given, the members of the tuple all have the same type.

So, we could do:

char get_elem_i(int i, std::array<char, 2> t)
{
    return t[i];
}

Here's a slight variant on the example you gave to show why it's not directly possible in the general case:

???? get_elem_i(int i, std::tuple<char, struct foo, class bar> t) {
    return std::get<i>(t);
}

What is the return type of that function? char? struct foo?


And you could always write a function like this:

char get_elem_i(int i, std::tuple<char, char> t) {
    switch (i) {
        case 0: return std::get<0>(t);
        case 1: return std::get<1>(t);
    }

    assert(false);
}

OTHER TIPS

If you can know the value of i at compile time then you can getting around specifying specifying the explicit value of i by wrapping your logic for i in aconstexpr.

For example:

#include <tuple>
#include <iostream>

constexpr int compile_time_known_i(int input) { return input / 3; }

template<int i>
char get_elem_i(std::tuple<char, char> t)
{
    return std::get<i>(t);
}

int main()
{
    std::tuple<char, char> t('H','i');
    char c = get_elem_i<0>(t);
    char d = get_elem_i<compile_time_known_i(3)>(t);
    std::cout << "The char is: " << c << " " << d <<  std::endl;
}

Given your i's can be known at compile time, this may help clean things up (although how much sense this makes depends on your use case).

If it's the syntax of passing a parameter you are after, you could achieve this with the preprocessor - with some optional type_traits safety.

#include <tuple>
#include <iostream>
#include <type_traits>

#define get_elem_i_ct(i, t)                                                                \
    std::get<i>(t);                                                                        \
    static_assert(std::is_integral<decltype(i)>::value, #i " must be an integral type");   \
    static_assert(std::is_same<decltype(t), std::tuple<char, char>>::value, #t " must be a tuple");

int main()
{
    std::tuple<char, char> t('H','i');
    char c = get_elem_i_ct(0, t)
    char d = get_elem_i_ct(1, t)
    std::cout << "The char is: " << c << " " << d <<  std::endl;
}

Although this achieves the stated syntactical requirement, I wouldn't recommend using this approach in anger - there's almost certainly a better solution to your actual problem.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top