Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deducing knowledge of original types, whilst simultaneously forwarding

Summary: I want to end up with a function that deduces the exact types it was called with and takes (e.g.) a tuple that forwards them (the types of which will be different from the exact types the function was called with).

I'm stuck trying to "know" via deduction the types of the arguments to a given function, whilst simultaneously forwarding them. I think I might be missing something crucial about 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'd like to see is a scenario where when my function (e.g. g1 or g2) gets called it knows and can use both the original types - int,double,void*,std::string&,const char* and the forwarded arugments too.

In this instance I don't seem to be able to find this information from within g1 or g2. The (deliberate, to print out the types) linker error shows me in g1 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 thing I don't get here:

  1. Why do none of the printed (via the linker error) types match what I actually passed in? (int,double,void*,std::string&,const char). Can I deduce what I actually was passed? Preferably with "natural" syntax, i.e. everything just once and nothing explicitly 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 that's "unwieldy" to say the least!

  2. In g1 the presence of && in the function signature declaration seems to alter the types in the template parameter Args itself. Compare that with:

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

    Or:

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

    using either of those with:

    int i;
    test(i);
    

    doesn't change the type of T. Why does the && change the type of T itself when & doesn't?

like image 764
Flexo Avatar asked Oct 25 '11 13:10

Flexo


People also ask

What information should be noted by the auditors during their inspection of securities on hand?

The auditor should consider the size of the entity, the entity's organizational structure, the nature of its operations, the types, frequency, and complexity of its derivatives and securities transactions, and its controls over those transactions in designing auditing procedures for assertions about derivatives and ...

What is the first step in preparation before commencement of new audit?

Steps before Commencement of new Audit 1. Appointment Confirmation The auditor must examine the terms and conditions of his appointment. It should be confirmed that the appointment was authorized by a competent authority and there is no reason whatsoever in rejecting this offer. 2.

What are the factors upon which reliable and relevant evidence depends on?

The reliability of evidence depends on the nature and source of the evidence and the circumstances under which it is obtained. For example, in general: Evidence obtained from a knowledgeable source that is independent of the company is more reliable than evidence obtained only from internal company sources.


1 Answers

Answer to first question:

Arguments to functions are expressions, not types. The difference between these two is expressed in chapter 5 [expr], p5:

If an expression initially has the type “reference to T” (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis.

Thus, there is no difference what-so-ever between g(str) and g(sref). g() always sees a std::string, and never a reference.

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

Answer to second question:

Template parameters of the form:

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

are special. They are unlike T, T&, or even const T&& in the following way:

When T&& binds to an lvalue, T is deduced as an lvalue reference type, otherwise T deduces exactly as per the normal deduction rules.

Examples:

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

This behavior is to support so called perfect forwarding which typically looks like:

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 one calls foo(A()) (an rvalue A), T deduces per normal rules as A. Inside of foo we cast t to an A&& (an rvalue) and call bar. The overload of bar that takes an rvalue A is then chosen. I.e. if we call foo with an rvalue, then foo calls bar with an rvalue.

But if we call foo(a) (an lvalue A), then T deduces as A&. Now the cast looks like:

static_cast<A& &&>(t);

which under the reference collapsing rules simplifies to:

static_cast<A&>(t);

I.e. the lvalue t is cast to an lvalue (a no-op cast), and thus the bar overload taking an lvalue is called. I.e. if we call foo with an lvalue, then foo calls bar with an lvalue. And that's where the term perfect forwarding comes from.

like image 196
Howard Hinnant Avatar answered Sep 21 '22 03:09

Howard Hinnant