Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 292
James Smith Avatar asked Oct 11 '20 07:10

James Smith


People also ask

What does decltype mean in C++?

The decltype type specifier yields the type of a specified expression. The decltype type specifier, together with the auto keyword, is useful primarily to developers who write template libraries. Use auto and decltype to declare a template function whose return type depends on the types of its template arguments.

What is the decltype of a function?

If the expression parameter is a call to a function or an overloaded operator function, decltype(expression) is the return type of the function. Parentheses around an overloaded operator are ignored. If the expression parameter is an rvalue, decltype(expression) is the type of expression.

What does decltype return?

decltype returnsIf what we pass to decltype is the name of a variable (e.g. decltype(x) above) or function or denotes a member of an object ( decltype x.i ), then the result is the type of whatever this refers to.

Is decltype runtime or compile time?

decltype is a compile time evaluation (like sizeof ), and so can only use the static type.


2 Answers

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.

like image 70
Enlico Avatar answered Oct 18 '22 01:10

Enlico


As Enrico explained, the type you were getting was a reference. In addition to his answer, I'd say you can get the actual type of the element of the tuple more easily with:

std::tuple_element_t<0, decltype(tup)>
like image 4
Pedro Boechat Avatar answered Oct 18 '22 00:10

Pedro Boechat