Item 1: Understanding the derivation of the template type

Source: Internet
Author: User

Item 1: Understanding the derivation of the template type

Some users of the complex system will ignore how it works and how it is designed, but it is nice to know something that it has done. In this way, the derivation of the template type in C + + has been a great success. Tens of thousands of programmers have passed parameters to the template function and obtained satisfactory results. Although many of those programmers find it hard to give more than a hazy description, such as how the derived function uses the type to derive.

If you are part of this, I have good news and bad news for you. The good news is that the derivation of the template type is the basis for one of the most amazing features of modern C + + (auto). If you are familiar with c++98 's template type derivation, you will also be familiar with how C + + uses Auto to deduce types. The bad news is that when the template type derivation rules are applied to auto, they sometimes look more difficult to understand than when applied to the template. For this reason, it is necessary to fully understand the various aspects of the template type derivation, because auto is based on this. This clause contains what you want to know.

If you are willing to browse a little pseudocode, we can look at the function template like this:

Template<typename t>void f (paramtype param);

The call to the function looks like this:

f (expr);

During compilation, the compiler infers two types with expr : One is T and the other is
Paramtype . These two types are often different, because paramtype often contain modifiers. (such as a const or referential qualification). For example, if a template declares this:

Template<typename t>void F (const t& param);

Then we use it this way:

int 0 ; F (x);

T is deduced as int, but paramtype is deduced as a const int&.

It is natural to think that the type of T is the type of argument passed into the function, that is, t is the type of expr . In the example above, x is int,T is deduced as int, but it doesn't always work like this. the derivation of the T type depends not only on expr , but also on Paramtype. Here are three things:

    • Paramtype is a pointer or reference, but not a universal reference (universal reference is described in #24, and now, what you need to know is that they exist, and the left and right references are not the same)
    • Paramtype is a universal reference.
    • Paramtype is not a pointer or a reference

So we have three types of derivation that need to be analyzed. Each one will be built on the basis of the template below, and so called:

Template<typename t>void  f (paramtype param); f (expr);
Scenario 1: The parameter is a pointer or reference, but not a universal reference

This is the simplest case where type deduction will work:

    1. If the type of expr is a reference, omit the referenced part.
    2. It then uses the type pattern of expr to match Paramtype to determine T.

For example, the following template:

Template<typename t>void f (t& param);

And we have these variable declarations:

int  - ; Const int cx = x; Const int& rx = x;

For various invocations, the type deduction forparam and T is used as follows:

f (x);   // T is int,        the type of param is int&F (CX)  ; // T is a const int,  the type of param is cosnt int&f (Rx  ); // T is a const int, the  type of param is a const int &

Note that in the second and third function calls, because CX and RX are const variables,T is deduced as cosnt int, so the Param type is const int&. This is important for callers. When they pass in the Const reference object parameter, they want the object to be immutable. That is, if the parameter is a reference-to-const. This is why it is safe to pass a const object to the template using the T & parameter: The Const property of the object is deduced as part of T .

Note In the third example, if the RX type is a reference,T is also deduced as a non-reference. This is because the 1th, when deduced, the reference attribute of the RX is ignored.

These examples show lvalue references, but the derivation of rvalue references is the same as the Lvalue reference.

If we change the F parameter from t& to const t&, the situation will change a little, but not unexpectedly. The const attribute of CX and Rx still exists, but since we now assume that Param is a const reference, it is not necessary to deduce the const attribute to T :

f (x);   // T is int,    the type of param is cosnt int&F (CX)  ; // T is int,    the type of param is cosnt int&F (Rx)  ; // T is int,    the type of param is cosnt int&

Similarly, the reference property of Rx is ignored.

If param is a pointer (or pointer to a const), the case and the reference are the same:

Template<typename t>void f (t* param); int  - ; Const int *px = &x;f (&x);  // T is int,        the type of param is int*f (px)  ; // T is cosnt int, the  type of param is const int*

Now you may feel a bit sleepy, because for reference and pointer types, the C + + type derivation rules are so natural that writing them out is so boring, because everything is obvious, just like the derivation system you want.

Scenario 2: Parameter is a universal reference

For a template with Universal reference parameters, the situation becomes complex. Most of these parameters are declared as rvalue references (for example, a function template takes a type T, and a universal reference is t&&), but they behave differently from passing in lvalue parameters. The complete situation will be discussed in item 24, but here are some general information:

    • If expr is an lvalue,T and param are deduced as lvalue references, where there are two unusual points. First, this is the only case where T is deduced as a reference. Second, although Paramtype is syntactically declared as an rvalue reference, his derivation type is an lvalue reference.
    • If expr is an rvalue, the rule for Case 1

As an example:

Template<typename t>voidF (t&&param);intx = -;Const intCX =x;Const int& rx =x;f (x); //x is the left value, so T is Int&//the type of param is also int&f (CX); //CX is an lvalue, so T is a const int&//the type of param is also const int&f (RX); //Rx is an lvalue, so T is a const int&//the type of param is also const int&F ( -);//27 is the right value, so t is int,//the type of param is int&&

Item 24 explains why This example works like this. The key point here is that the type deduction rules referenced by Universal are different for lvalue and rvalue values. In particular, when the universal reference is in use, the type deduction is differentiated based on whether the value passed in is an lvalue or an rvalue. This is never the case with non-universal references.

Case 3: The parameter is not a pointer and is not a reference

When Paramtype is not a pointer and is not a reference, we handle the value of the pass (by-value) Condition:

Template<typename t>void f (T param);

This means thatparam will become a copy of the incoming parameter, a completely new object.

    1. As before, if the type of expr is a reference, the reference property is ignored.
    2. If, after ignoring the reference property of expr ,expr is const, and the Const property is ignored. If it is volatile, it is also ignored. (volatile objects are not common, they are commonly used to implement device drivers, in detailed cases, discussed in item40)

So:

int  -  int cx = x; Const int& rx = x;f (x);    // T and param type are the same, int f (CX);   // T and param type are the same, int f (RX);   // T and param type are the same, int

Remember, although CX and RX are const variables,param is not const. This is meaningful,param is a completely separate object from CX and RX-from the copy. In fact, the CX and RX can be changed and param can be changed without connection, which is why the const attribute ofexpr is ignored when deriving the type. Just because expr can't be changed doesn't mean that their copy can't be changed.

It is important that you know that the const (and volatile) attribute is ignored only for the pass-to-value (by-value) parameter. As we can see, the Const property of the References-to or pass pointer (pointers-to) parameter is preserved when the type is deduced. But let's think about it. Expr is a case where the cosnt pointer points to a const type, and expr is a pass-value (by-value) parameter:

Template<typename t>voidchar"fun withpointers"   //T is a const CHAR,PARAM type is const char*

Here, the const on the right of the asterisk declares that PTR is const: PTR cannot point elsewhere, nor can it be set to null. (The const to the left of the asterisk means that a---string that ptr points to---is const and therefore cannot be modified) when the PTR incoming f,ptr copies a copy and assigns it to param, so The pointer itself (PTR) will be passed in as a value (By-value). The same type derivation rule, the const attribute of the By-value parameter PTR will be ignored, so * theparam type will be deduced as cosnt Char, A pointer that points to a const character type that can be changed.

Array parameters

Before the ones that have been designed to most mainstream template parameters are deduced, but here are some small cases where we go straight to the understanding. Array types are different from pointer types, although sometimes they appear to be interchangeable. This illusion comes mainly from: In many cases, an array can degenerate into a pointer to its first element (decays). This degradation allows code to be written like this:

Const Char " J.p.briggs " ; Const Char* ptrtoname = name;

Here, a const char* pointer ptrtoname is initialized with the array *name of the const CHAR[13] . These types (cosnt char and const CHAR[13]) are different, but because of Array-to-point's degenerate rules, the code can be compiled.

However, what happens when you pass array values (By-value) into a function:

Template<typename t>void  f (T param); F (name);

We start with a function without an array parameter, yes, yes, this syntax is legal:

void myFunc (int param[]);

But the declaration of an array is treated as a pointer declaration, which means that myfunc can be declared equivalently:

void myFunc (int* param);

This equivalence to arrays and pointer parameters is from the C side, and this increases the illusion that arrays and pointers are the same.

Because an array parameter declaration is treated as a pointer parameter, an array passed into the template function by passing the value (By-value) is deduced as a pointer type. This means that the above parameter T is deduced as a const char*:

f (name);    // name is an array, but T is deduced as cosnt char*

However, now there is a problem, although the function cannot declare real array parameters, but they can declare reference parameters to the array! So, if we modify the function to pass the reference (by-references):

Template<typename t>void f (t& param);

And we pass in an array:

f (name);

The type deduction for T will be the true array type! This type contains the size of the array, so in this example,T is deduced as cosnt char[13], and the type of param (a reference to the array) is cosnt char (&) [13]. Yes, the syntax looks poisonous, but knowing the rules will give you a little bit more attention.

Interestingly, declaring a reference to an array allows us to create a template to deduce the size of the array:

// returns the array size at compile time (the array parameter has no name, because we only know the size of the array)template<typename T, std::size_t n>cosntexpr std::size_t ArraySize (T (&) [N]) noexcept{    return  N;

A function that declares such a constexpr in item15 allows the result to be returned at compile time. This allows us to initialize the new array with the size of one array for the size of another new array:

int 1 3 7 9  One  A  * }; int mappedvals[arraysize (keyvals)];

Of course, as a modern C + + developer, you might prefer a std::array to create an array:

std::array<int, arraySize (keyvals) > MAPPEDVALSL;

For ArraySize to be declared as noexcept, this can help the compiler produce better code. If you want to know the details, you can see item 14.

function parameters

In C + +, arrays are not the only ones that degenerate into pointers, function types degenerate into function pointers, and all of our discussion of the derivation of our array is applicable to function type deduction:

void somefunc (intdouble); template<typename t>void  F1 (T param); template<typename t>void f2 (t& param); F1 (   somefunc) ; // T is void (*) (int, double) F2 (somefunc);    // T is void () (int, double)

This may seem almost no different, but if you already know the degradation of array-to-pointer, you will also know the degeneration of function-to-pointer.

Now you get it: the rules of function type derivation, I said at the very beginning they are fairly simple, and in most cases this is the case. For universal references, the type of left worth derivation requires special treatment, which makes us a little dizzy, but the rule of degenerate into a pointer (Decay-to-pointer) makes us more dizzy. Sometimes, you simply want to grab your compiler and crave: "Tell me what type you derive". When this happens, go to item 4, because this article focuses on cheating the compiler into doing what you want it to do.

What you have to remember.
    • When the template type is deduced, the parameters of the references type are treated as non-references. This means that the reference property is ignored.
    • When the reference parameter of the universal type is deduced, the Lvalue parameter is treated in a special way.
    • The cosnt and/or volatile parameters are treated as non-const and non-volatile when deducing the value of the By-value type parameter.
    • When the inferred type is an array or a function, it is degenerate into a pointer, unless the formal parameter is a reference.

Item 1: Understanding the derivation of the template type

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.