"Effective modern C + +" translation-clause 1: Understanding template Type Derivation

Source: Internet
Author: User
Tags volatile

北京2016年1月9日13:47:17 开始第一章的翻译。第一章名为 类型推断分为四个条款:1理解模板类型推导2理解auto自动类型推导3理解decltype操作符4如何对待推导的类型

Chapter One type derivation

C++98 has a single type derivation rule used to derive function templates. C++11 slightly modified these rules and added two deduction rules, one for auto and one for Decltype. The c++14 then expands the context in which auto and decltype can be used. The universal application of type derivation frees programmers from the obvious superfluous types that must be spelled, making C + + software development more resilient, because changing a type somewhere will automatically propagate to other places through type derivation. It makes the C + + software more adaptable because changing one type in the source code automatically spreads through the type deduction to other places. However, it can make the code more difficult to infer because the compiler infers that the type may not be as obvious as we thought.

To make efficient programming in modern C + +, you must have a solid understanding of type deduction operations. Because there are too many situations you will use it: in the invocation of the function template, in most of the scenes in auto, in the Decltype expression, in the c++14 when the mysterious Decltype (auto) constructs are applied.

This chapter provides some basic information about type derivation that every C + + developer needs to understand, explaining how template type derivation works, how auto builds its own rules on this basis, and how Decltype works according to its own independent rules. It even explains how you force the compiler to make the result of the type deduction visible, allowing you to determine that the compiler's results are what you want.

Article 1: Understanding template Type Deduction
Some people say that imitation is the most sincere form of flattery, but the ignorance of happiness can be equally heartfelt praise. When a user uses a complex system, ignoring how its system is designed and how it works, you will still be happy with what it's done, and in this way, the type derivation of templates in C + + has been a huge success, with millions of of programmers passing parameters to template functions, And get a completely satisfying answer, though many programmers are forced to pay more than a hazy description of how these functions are deduced.

If the people mentioned above include you, I have a good news and a bad news. The good news is that the type deduction rules and templates for the variables that auto declares are essentially the same, so when it comes to auto, you are familiar (see clause 2). The bad news is that when the template type derivation rules are applied to auto, you are likely to be surprised at what's going on, and if you want to use auto (you should certainly use auto as opposed to the exact type declaration, see clause 5), you need to have a reasonable and correct understanding of the template type derivation rules, They are usually straightforward, so it's not too much of a challenge, and the way you work in c++98 is the same, and you probably don't need to think too much about it.

If you are willing to ignore a small amount of pseudo-code, we can think directly about the code of the following template function:

temppate<typename T>void f(ParamType param);

This can be called:

f(expr);  //call f with some expression

At compile time, the compiler infers two types according to expr: one is T and the other is Paramtype. These two types are often different because paramtype often include some modifiers such as const or a qualifier that is a reference. For example, if the template is declared like this:

temppate<typename T>void f(const//ParamType is const T&

We call this:

int x = 0;f(x); //call f with an int

T is deduced to be of type int, but Paramtype is deduced as a const int& type.

Naturally, it is expected that the type derivation T is the same as the type of the argument passed to the function, just as T is the type of expr. In the example above, x is the int type, and T is deduced to be of type int. But that's not always the case. The type of T being deduced depends not only on expr, but also on the type of Paramtype. There are three types of cases:

? Paramtype is a pointer or reference type, but not a universal reference (universal reference is described in clause 26. At the moment, you just have to know the existence of universal reference.

? Paramtype is a universal reference.

? Paramtype is neither a pointer nor a reference.

Therefore, we will have three types of deduced scenarios, each of which will be based on our common template form:

template<typename T>void f(ParamType param);f(expr);   //deduce T and ParamType from expr

The first case: Paramtype is a pointer or reference type, but not a universal reference

The simplest case is when paramtype is a pointer or reference type, but not universal reference. In such a case, the type deduction works like this:

? If expr is a reference type, the reference section is ignored.

? The type of the Paramtype is determined by the type of pattern-matching expr to determine the types of t

For example, if our template is this:

template<typename T>void f(T& param);  //param是一个引用

Also, we declare variables as follows:

int27;        //x is an intconstint cx = x;  //cx is a const intconstint//rx is a read-only view of x

When a function is called, the types of param and T are deduced as follows:

f(x);              is int, param‘stypeis int&f(cx);             is const int, param‘stypeis const int&f(rx);             is const int, param‘stypeis const int&

In the second and third function calls, note that because CX and RX are assigned as const, T is deduced as const int, so the resulting parameter type is const int&. This is very important to the caller. When they pass a const object to a parameter of a reference type, they expect that the object will still not be modified, for example, the type of the parameter is deduced as a reference to a const. This is why it is safe to pass a const object to a template with a t& parameter, and the constant nature of the object becomes part of the deduced type T.

In the third example, note that although RS is a reference type, T is deduced as a non-reference type because the reference (reference-ness) of RS is ignored in the derivation process, if not (for example, T is deduced to be const int&), The type of param will be const int&& a reference reference to reference is not allowed in C + +, and the only way to avoid them is to ignore the referential nature of the expression when type deduction.

These examples are reference parameters to Lvalue, but these type deduction rules apply to Rvalue reference parameters, and of course, only the right value of an actual delegate is passed to a reference of an rvalue type, but this has no effect on the type deduction.

If we change the parameter type of F from t& to Const T&, things will send a little bit of a change, but it won't be so surprising. The constants of CX and Rx are still satisfied, but since we now assume that Param is a constant reference, the const does not need to be inferred as part of T:

template  <typename  t>void  F (const  t& param); //param is now a ref-to-const  int  x = 27 ; //as before  const  int  cx = x; //as before  const  int  & rx = x; //as before  f (x); //t is int, param ' s type is const int&  f (CX); //t is int, param ' s type is const int&  f (RX); //t is int, param ' s type is const int&   

As in the previous case, the RX's citation was ignored at the time of type deduction.

If Param is a pointer type (or a pointer to a constant) instead of a reference type, the type deduction is done in the same way.

is27;             //as beforeconst int *px = &x;     istoof xf(&x);                   is int, param‘stypeis int*f(px);                   is const int, param‘stypeis const int*

At this point, you may find yourself yawning and nodding, because C + + 's type derivation rules are so common to arguments for reference and pointer types that it is a dull thing to see them written out one after the other. Because they are so obvious, and you expect in the type deduction is the same.

Second case: Paramtype is a universal reference

When it comes to universal reference as a template parameter (for example, the t&& parameter), things become less clear because the rules have special treatment for lvalue parameters. The full content will be covered in article 26, but here is a summary version:

? If expr is an lvalue, T and Paramtype are deduced as a reference to an Lvalue

? If expr is an rvalue, use the usual type deduction rules

For example:

Template<typename t>void f (t&& param); Param isNow a universal referenceint x = -;       as Beforeconst int cx = x;      As beforeconst int& rx = x;                   As Beforef (x); X isLvalue, so T isInt&, param' s type  isAlso Int&f (CX); Cx isLvalue, so T isconst Int&,//param' s type  isalso const INT&AMP;F (RX); Rx isLvalue, so T isconst Int&,//param' s type  isalso const INT&AMP;F ( -); // -  isRvalue, so T isint, param' s type  istherefore int&&

Clause 26 explains in detail why, but what is important now is that the type deduction is different for the template when the argument is univsersal references and the parameter is an lvalue or rvalue. When univsersal references is used, the type deduction rules distinguish between Lvalue and rvalue, which never occurs on Nivsersal references references.

The third case: Paramtype is neither a pointer nor a reference

When Paramtype is neither a pointer nor a reference, we handle it by passing by value:

template<typename T>void f(T param);     //param is now passed by value

What this means is what the boredom is passing, and Param will become a copy of it – completely a new object. In fact, Param is a brand-new object control that derives the rules derived from expr in T:

? As before, if the type of expr is a reference, then the reference section is ignored.

If, after the referential nature of expr is ignored, expr has a const modifier, ignoring the const, and, if with a volatile modifier, also ignores (volatile objects are unusual objects, they are usually used only to implement device drivers, more details can be referred to clause 42).


int27;             //as beforeconstint cx = x;       //as beforeconstint& rx = x;      //as beforef(x);                   //T and param are both intf(cx);                  //T and param are again both intf(rx);                  //T and param are still both int

Note that although CX and Rx represent constants, param is not a constant. That's a lot of sense. Because Parm is a completely separate object from Cx,rx, it is a copy of CX and RX. In fact, CX and RX cannot be modified and Param can be modified without any relationship, which is why the constant nature of expr is ignored when deriving the Param type, since expr cannot be modified does not mean that its copy cannot be modified.

It is important to note that the const is ignored only in parameters passed by value. As we can see, for reference and pointers to constants, the constant nature of expr is preserved at the time of type deduction. But consider the following case, expr is a constant pointer to a const object, and expr is passed to a parameter by value:

template<typename T>void f(T param);                             //isby valueconstconst"Fun with pointers"//isconstconst objectf(ptr);                                      //ofconstconst

Here, the const on the right of multiplication sign declares PTR as const, meaning that PTR cannot point to a different location, nor can it be set to null (the const on the left of multiplication sign means that the string that PTR points to is const, so the string cannot be modified). When PTR is not passed to F, the pointer is copied to the Param by bit. Therefore, the pointer itself (PTR) will be passed by value, and the constants of PTR will be ignored, according to the type deduction rules passed by value, the Param type is deduced as const char*, a pointer can be modified at the point where it is pointed, but the string pointed to cannot be modified. The constants referred to by PTR are preserved at the time of type derivation, but the constant nature of PTR itself is ignored when creating new pointer param by copying.

Array as a parameter

This almost covers the derivation of its mainstream template type, but there is a side-flow case that is worth knowing. Although array types and pointer types are sometimes interchangeable, they are different. A major contributor to this illusion is that in many cases an array is degraded into a first element that points to it. This degradation allows such code to be compiled:

constchar"J. P. Briggs"//name‘s type is const char[13]constchar* ptrToName = name;       //array decays to pointer

Here, the string pointer ptrtoname of the constant type is initialized to name, and name is a constant array, which is equivalent to an array with 13 constant character elements. These types (const char* and const CHAR[13]) are not the same, but because of the degradation of arrays to pointers, such code can be compiled through.

But what if the template's argument is an array type that is passed by value? So what's going to happen?

template<typename T>void f(T param);       //withby-value parameterf(name);               //forand param?

We should first note that there is no parameter of the array type in the parameter of the function, and yes, the following syntax is legal

void myFunc(int param[]);

However, the array declaration at this point is looked at as if it were a pointer declaration, which means that the function MyFunc can be declared equal to:

void myFunc(int//same function as above

The equivalence of arrays and pointers on parameters stems from the illusion that C + + is created based on C and that the array and pointer are equivalent in type.

Because the declaration of an array parameter is treated as a pointer declaration, an array passed by value to a template parameter is deduced as a pointer type, which means that the type of the parameter T is deduced as a const char* in the call of the template function F below:

f(name);                isas const char*

But now there's a curve ball, though the function can't declare a real array-type parameter, but they can declare a reference to the array, so if we change the template F to pass the argument by reference:

template<typename T>void f(T& param);     //template with by-reference paremeter

We then pass an array to him:

f(name);              arrayto f

The type of T is deduced as the type of the array. This type includes the size of the array, so in the above example, T is deduced as the type of the const CHAR[13],F parameter (a reference to an array) is a const char (&) [13]. Yes, this syntax seems to be harmful, but on the good side, knowing this will reward you for those rare points that others don't get (knowing that it's good for you).

Interestingly, declaring a reference to an array allows us to create a template that returns the length of the array, which is the number of elements that the array has:

template<typenamestd::size_t N>       //return size ofconstexprstd//an array as a{                                         //compile-time   return N;                              //constant}

Note that the use of constexpr (see Clause 14) allows the result of the function to be obtained during compilation, which allows us to declare that the length of an array is the same as the length of another array

int1379112235// keyVals has                                            // 7 elementsint mappedVals[arraySize(keyVals)];         // so does                                             // mappedVals

Of course, as a modern C + + developer, you should be more accustomed to using std::array instead of built-in arrays:

std::array<int, arraySize(keyVals)>// mappedVals‘                                                // size is 7

function as a parameter

In C + +, an array is not the only entity that can degenerate into a pointer. The function type can also degenerate into a function pointer, and any of the rules that we discuss about the type derivation and the array-related things that apply to the function's type deduction, the function type degrades to a pointer to the function. So:

voidSomeFunc (int,Double);//SomeFunc is a function;                            //type is void (int, double)Template<TypeNameT>voidF1 (T param);//In F1, Param passed by valueTemplate<TypeNameT>voidF2 (t& param);//In F2, param passed by refF1 (SomeFunc);//param deduced as Ptr-to-func;                             //Type is void (*) (int, double)F2 (SomeFunc);//param deduced as Ref-to-func;                            //Type is void (&) (int, double)

These and arrays are actually not different. But if you want to learn about the degradation of arrays to pointers, you should also understand that function-to-pointer degradation is better.

So, here you should know the rules of template type derivation, and at the very beginning I said they were so simple and clear. In fact, for most rules, it is true that the only thing that can spark a splash is when using universal references, the Lvalue has a special treatment, and even the degenerate rules of arrays and functions to pointers can make water cloudy. Sometimes, you may simply grab your compiler, "tell me what type of export you are pushing." When this happens, turn to clause 4, as it is dedicated to tricking the compiler into doing so.

Please remember:

? When the template's arguments are pointers or references, but not universal reference, the initialized expression is a reference that is ignored.

When the parameter of the template is universal reference, the argument of the left value produces a reference to the Lvalue, and the argument to the right value produces an rvalue reference.

The parameters of the template are passed by value, and the instantiation and the constants of the instantiated expressions are ignored.

? During type deduction, arrays and functions are degraded to pointer types unless they are initialized to a reference.

"Effective modern C + +" translation-clause 1: Understanding template Type Derivation

Related Article

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.