How does std :: get work?

After trying to make the std::get<N> (std::tuple) method, I'm not sure how it is implemented by compilers. I know that std::tuple has such a constructor,

 tuple(Args&&... args); 

But what exactly is assigned to args... ? I think this is useful in order to know how std::get works, because the arguments must be placed somewhere to access them ...

+4
source share
1 answer

Here is a rough tuple class implementation toy.

Firstly, some metaprogramming pattern to represent a sequence of integers:

 template<int...> struct seq {}; template<int max, int... s> struct make_seq:make_seq< max-1, max-1, s... > {}; template<int... s> struct make_seq<0, s...> { typedef seq<s...> type; }; template<int max> using MakeSeq = typename make_seq<max>::type; 

Next, a class tag that actually stores data:

 template<int x, typename Arg> struct foo_storage { Arg data; }; 

This tagging method is a general scheme whenever we want to associate data with some tag at compile time (in this case, an integer). The tag ( int here) is usually not used anywhere in the repository, it is simply used to mark the repository.

foo_helper decompresses the sequence and set of arguments into a bunch of foo_storage and inherits them linearly. This is a fairly common pattern - if you do this a lot, you will end up creating metaprogramming tools that will do this for you:

 template<typename Seq, typename... Args> struct foo_helper {}; template<int s0, int... s, typename A0, typename... Args> struct foo_helper<seq<s0, s...>, A0, Args...>: foo_storage<s0, A0>, foo_helper<seq<s...>, Args...> {}; 

My crude tuple type, foo , creates a sequence index and args sequence package and passes it to the helper above. Then the helper creates a bunch of tagged data containing the parent classes:

 template<typename... Args> struct foo: foo_helper< MakeSeq<sizeof...(Args)>, Args... > {}; 

I removed everything from the body of foo because I do not need to implement get .

get pretty simple: we take the storage type (not the tuple type), and the explicit argument template N removes the ambiguity which we are going to get from foo_storage<n, T> . Now that we have the storage type, we simply return the data field:

 template<int N, typename T> T& get( foo_storage<N, T>& f ) { return f.data; } template<int N, typename T> T const& get( foo_storage<N, T> const& f ) { return f.data; } 

We use C ++ langauge overload mechanisms for heavy lifting. When you call a function with an instance of a class, this instance, since each of the parent classes has gone over to check if they can be matched. For fixed N there is only one parent class, which is a valid argument, so the parent class (and therefore T ) is automatically displayed.

And finally, some basic test code:

 #include <iostream> int main() { foo<int, double> f; get<0>( f ) = 7; get<1>( f ) = 3.14; std::cout << get<0>(f) << "," << get<1>(f) << "\n"; } 
+7
source

Source: https://habr.com/ru/post/1486927/


All Articles