Derivation of knowledge about source types, while simultaneous forwarding

Summary. I want to get a function that outputs the exact types with which it was called, and accepts (for example) a tuple that forwards them (the types of which will differ from the exact types with which the function was called).

I was stuck trying to "find out" through deduction of the types of arguments of this function, while sending them. I think that maybe I am missing something important in how this works.

#include <tuple> #include <string> #include <functional> template <typename ...Args> struct unresolved_linker_to_print_the_type { unresolved_linker_to_print_the_type(); }; void f(int,double,void*,std::string&,const char*) { } template <typename F, typename ...Args> void g1(F func, Args&&... args) { unresolved_linker_to_print_the_type<Args...>(); auto tuple = std::forward_as_tuple(args...); unresolved_linker_to_print_the_type<decltype(tuple)>(); } template <typename F, typename T, typename ...Args> void g2(F func, const T& tuple, Args... args) { unresolved_linker_to_print_the_type<Args...>(); unresolved_linker_to_print_the_type<decltype(tuple)>(); } int main() { int i; double d; void *ptr; std::string str; std::string& sref = str; const char *cstr = "HI"; g1(f, i,d,ptr,sref,cstr); g2(f, std::forward_as_tuple(i,d,ptr,sref,cstr), i,d,ptr,sref,cstr); } 

What I would like to see is a scenario where my function (for example, g1 or g2 ) is called, it knows and can use both the original types - int,double,void*,std::string&,const char* , so and redirected measures.

In this case, I seem to be unable to find this information from g1 or g2 . ((Intentional to print types) The linker error shows me in g1 that they are:

 int&, double&, void*&, std::string&, char const*& int&, double&, void*&, std::string&, char const*& 

and in g2 :

 int, double, void*, std::string, char const* int&, double&, void*&, std::string&, char const*& 

There are two things that I cannot get here:

  • Why does not one of those printed (through linker errors) match what I really transmitted? ( int,double,void*,std::string&,const char ). Can I understand what I actually got? Preferably with a "natural" syntax, i.e. All only once and nothing is clearly written out. I can explicitly write:

     g2<decltype(&f),decltype(std::forward_as_tuple(i,d,ptr,sref,cstr)),int,double,void*,std::string&,const char*>(f,std::forward_as_tuple(i,d,ptr,sref,cstr),i,d,ptr,sref,cstr); 

    but it is "cumbersome", to say the least!

  • In g1 presence of && in the function signature declaration seems to change the types in the Args template parameter. Compare this to:

     template <typename T> void test(T t); 

    Or:

     template <typename T> void test(T& t); 

    using any of them with:

     int i; test(i); 

    does not change type T Why does && change type T when & does not work?

+8
c ++ c ++ 11 forwarding
source share
2 answers

The answer to the first question:

Function arguments are expressions, not types. The difference between the two is expressed in chapter 5 [expr], p5:

If the expression is initially of type "reference to T" (8.3.2, 8.5.3), the type is brought to T before any further analysis.

So there is no difference between g(str) and g(sref) . g() always sees a std::string and never refers.

Additionally, expressions can be lvalue or rvalue (this is actually a simplification of C ++ 11 rules, but it is close enough for this discussion - if you want the details to be in 3.10 [basic.lval]).

The answer to the second question:

Form template options:

 template <class T> void g(T&&); 

are special. They differ from T , T& or even const T&& as follows:

When T&& associated with an lvalue, T is inferred as a reference type of lvalue, otherwise T is inferred exactly in accordance with normal subtraction rules.

Examples:

 int i = 0; g(i); // calls g<int&>(i) g(0); // calls g<int>(0) 

This behavior supports the so-called perfect forwarding, which usually looks like this:

 struct A{}; void bar(const A&); void bar(A&&); template <class T> void foo(T&& t) { bar(static_cast<T&&>(t)); // real code would use std::forward<T> here } 

If you call foo(A()) (rvalue A ), T prints to normal rules like A Inside foo we apply T to A&& (rvalue) and call bar . Then the overload bar is selected, which takes the value r A That is, if we call foo value of r, then foo calls bar with rvalue.

But if we call foo(a) (lvalue A ), then T is output as A& . Now the cast is as follows:

 static_cast<A& &&>(t); 

which is simplified by the rules of collapsing links:

 static_cast<A&>(t); 

those. lvalue T is passed to lvalue (no-op cast), and therefore the bar overload is called, which takes an lvalue. That is, if we call foo with lvalue, then foo calls bar with lvalue. And that is where the term perfect forwarding comes from.

+2
source share

types (even in C ++) is basically a concept of compilation type (except, of course, RTTI in vtables).

If you need fully dynamic types, then C ++ may not be the best language for this.

Perhaps you can extend GCC (actually g++ , assuming it is at least 4.6) with a plugin or GCC MELT (MELT is a high-level domain language for the GCC extension) that does what you want (for example, by providing an additional built-in code that encodes the type of its arguments in some constant string, etc.), but that requires some work (and applies to GCC).

But I don’t understand why you want to do such baroque things in C. If dynamic typing is important to you, why not use a dynamically typed language?

0
source share

All Articles