C ++ template argument type inference

Given the template function declared as follows:

template<class T> int Function(T object); 

The user can call this function by specifying a template type, for example:

 int result = Function<float>(100.f); // Valid 

But the type specification is optional, since the compiler can infer the type T from the specified argument type; eg:

 int result = Function(100.f); // Also valid, the compiler deduced the type "float" from the literal type 

Let's say I'm getting a little complicated, and I want a templated parameter value as follows:

 template<class T, T* object> int Function(); 

I can call my function as follows:

 static float val = 100.f; // ... int result = Function<float, &val>(); 

My question is: is there a way that I force the compiler to infer a type T based on the argument type & val?

I need a way to make the following code valid:

 static float val = 100.f; // ... int result = Function<&val>(); 

Can this be done?

+10
source share
2 answers

In C ++ 17, you can have non-piggy type auto template parameters. This will allow you to solve your problem.

Sort of:

 template<auto object, class T=std::decay_t<decltype(*object)>> int Function(); 

(if you want type T in Function body)

In C ++ 14, the C ++ 17 function is missing. It was added precisely because it was missing. Workarounds include macros like #define UGLY_HACK(...) decltype(__VA_ARGS__), __VA_ARGS__ .

+11
source

Note: the answer here was borrowed from efficient modern C ++ with (very) few additions of my own

This is one of those questions that are easy to ask but difficult to answer! I remember reading the whole chap. After deducing the type of the template and for the novice reader, the answer is not clear in any reading. Nevertheless, I will try to clarify this here.

It should be noted that there is something called Universal References (which do not match references or references to r-values) that affects template type inference, and I assume that readers are aware of references to l-value and r-value,

Any definition of the ubiquitous function template is as follows:

 template <typename T> returnType function(paramType param); 

The function call will look something like this:

 function(expression); 

The compiler uses an expression to determine the type of T and the type of paramType . This is because most often paramType contains decorations such as const , const & , const & amp; etc. Beginners will be tempted to believe that the type T output by the compiler will be the same as the type of the expression , that is, the argument passed to the function, but this is not always the case. A residue of type T depends on both the expression and paramType . Depending on what the paramType function parameter is , there are three things to consider when inferring a template type:

  1. paramType is a pointer or reference, but not a universal reference
  2. paramType is a universal link
  3. paramType is neither a pointer nor a reference.

Let's look at each case one by one

Case 1: paramType is a pointer or link, but not a universal reference

Call me crazy, but this is the simplest case that you can meet. In this case, the type deduction works like this: (i) If the expression is a reference, ignore the reference part (ii) then match the expression template with paramType to determine T

Let's look at an example:

 template <typename T> returnType function(T &param); 

We have the following variable declarations:

 int x = 23; // x is int const int const_x = x; // const_x is const int const int& ref_x = x; // ref_x is a reference to x as const int 

The derived call for T and param in various calls is as follows:

 f(x); //T is int, param type is int& f(const_x); //T is const int, param type is const int& f(ref_x); //T is const int, param type is const int& 

Two points should be noted here:

(i) the -ness link is ignored by the compiler to determine the type here

(ii) const -ness becomes part of type T when passing a const object or a reference to a const object and, therefore, passing const objects or references to a const object in functions that take the T & amp; safely.

If we change the function parameter with T & amp; on const T & amp; , because in this case we assume that param is a reference to const, then const -ness does not need to be output as part of T. The following is an example:

 template <typename T> returnType function(const T& param); // param is now a ref-to-const int x = 23; // same as previous const int const_x = x; // same as previous const int& ref_x = x; // same as previous f(x); // T is int, paramType is const int& f(const_x); // T is int, paramType is const int& f(ref_x); // T is int, paramType is const int& 

Note : the variable 'x' is not a constant argument for 'f ()', but until then it is output as a constant parameter

If paramType is a pointer, then everything will work basically the same as with links, just instead of links there will be pointers. For example, for completeness, below:

 template <typename T> returnType function( T* paramType); //paramType is now a pointer int x = 23; // same as before const int *pointer_x = &x; // pointer_x is pointer to x as const int f(&x); // T is int, paramType is int* f(&pointer_x); // T is const int, paramType is const int* 

For completeness, I can also post the case if paramType was a pointer to a constant object, as shown below:

 template <typename T> returnType function(const T* paramType); int x = 23; // same as before const int *pointer_x = &x; // pointer_x is pointer to x as const int f(&x); // T is int, paramType is const int* f(&pointer_x); // T is int, paramType is const int* 

That is, again const -ness is no longer output as part of T

In the case of references to r-values, residues of type T and paramType follow basically the same rules as in the case of references to l-values.

This covers most of this for the first case. Let's look at our case 2.

Case 2: paramType is a universal reference

Universal references are declared as references to r-values, but accept an l-value, but their behavior is different in that the arguments of the function receive references to an l-value. Here's how type inference works in this case:

(i) If the expression is an l-value, both T and paramType are output as an l-value. (This looks odd considering the way the code looks, because although paramType is declared using r-value reference syntax, its inferred type has a reference to l-value.) It should be noted that this is the only case when T is output as a link.

The example below explains my explanation:

 template <typename T> returnType function(T&& paramType); //param becomes universal reference if // argument to function call is an l-value int x = 23 // same as previous const int const_x = x; // same as previous const int& ref_x = x; // same as previous f(x); // x is l-value therefore T is int& // paramType is int& f(const_x); // const_x is l-value therefore T is const int& //paramType is also const int& f(ref_x); // ref_x is l-value therefore T is const int& //paramType is also const int& f(23); // 27 is r-value so T is int // paramType is now int&& 

I want to be honest here and say that this does not explain why universal links work the way they are, but I think this post will get too long if I continue to justify it here.

Case 3: paramType is neither a pointer nor a reference ##

Here, the transfer by value in the template occurs, which means that param will be a copy of everything that is passed into the argument of the calling function, that is, a completely new object, and this motivates the rules that control the derivation of type T from the expression . Two points should be noted here:

(i) ignore the -ness link in the expression , if any.

(ii) after ignoring ref -ness, ignore also const -ness or volatile -ness, i.e. if he is present

 template <typename T> returnType function(T paramType); int x = 23; const int const_x = x; const int& ref_x = x; f(x); // T and paramType are both int f(const_x); // T and paramType are both int here too f(ref_x); // T and paramType are both int again 

Note that although const_x and ref_x are const objects that cannot be changed, this does not mean that copies of them cannot be changed. It looks simple, but it gets harder when we pass a constant pointer to a constant object. Let's look at another example:

 template <typename T> returnType function(T param); const double *const dPtr = 23; //dPtr is const pointer to const double function(dPtr); //passing argument of type const double *const 

When the const pointer is passed by value, const -ness is lost, and the pointer is copied by value, which synchronizes with the type inference rules for passing by value, but the const -ness of what the to pointer points to is saved and, therefore, paramType will be const * double .

This can cause you to feel dizzy, as it happened to me when I started learning about it. The best way would be to re-read it and try to encode.

+7
source

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


All Articles