c++typestemplate-meta-programmingstdtuple

What exactly is decltype(std::get<0>(tup))?


I am currently learning metaprograming in C++, and I'm trying to see whether an element of a tuple is a pointer. I tried this approach:

int a = 3, b = 4;
auto tup = std::make_tuple(&a, b);
std::cout << std::is_pointer<decltype(std::get<0>(tup))>::value; //prints 0

I thought this was strange, so I examined the type clang deduced (I'm using clang-10), which is

__tuple_element_t<0UL, tuple<int *, int>

And it looks like some internal type.

Why do I get this weird type and what would be the proper way to get the actual type of an element of the tuple? I have only a solution which uses an intermediate auto variable, but is hardly optimal.


Solution

  • std::is_same/std::is_same_v can be very helpful in TMP and, when looking for types being equal to other types, it's invaluable when used in conjunction with static_assert.

    With the following code you can see that std::get gives you a reference to the element of the tuple (as confirmed by cppreference's page on std::get), in this case int*&, where int* is the type of the element. If you use it to initialize another variable you get a copy of it (so no more reference for elem, just int*), just like int x = r; defines x to be a copy of r regardless of r being a reference or not.

    #include <type_traits>
    #include <tuple>
    
    int main() {
        int a = 3, b = 4;
        auto tup = std::make_tuple(&a, b);
        auto elem = std::get<0>(tup);
        static_assert(std::is_same_v<decltype(elem), int*>,"");
        static_assert(std::is_same_v<decltype(std::get<0>(tup)), int*&>,"");
    }
    

    As regards your attempt, the fact that the second static_assert above is passing, explains why std::is_pointer<decltype(std::get<0>(tup))>::value prints false/0: that is a reference to int*, not an int*. On the other hand, the following does print true/1:

    std::cout << std::is_pointer_v<std::remove_reference_t<decltype(std::get<0>(tup))>>;
    

    See that I've used is_pointer_v instead of is_pointer and is_same_v instead of is_same? Those with _v are helper metafunctions that give you the value member of the non-_v metafunctions. remove_reference_t works similarly with respect to remove_reference, but giving the type member.