"Effective modern C + +" translation-Clause 4: Learn how to view inferred types

Source: Internet
Author: User
Tags types of functions

Article 4: Learn how to view the inferred types

Those who want to understand how the compiler derives the type are usually divided into two camps. The first kind of camp is pragmatism. Their motivation usually comes from the programming process (for example, they are still debugging), they use the compiler to find, and believe that this can help them find the root cause of the problem. The second is the experiential, who are exploring the derivation rules described in clause 1-3. and confirm the results of their predictions from a large number of deduction scenarios ("For this code, I think the deduced type will be ..."), but sometimes they just want to answer simply if so, what happens? They might want to know if I use a universal reference (see clause 26) Instead of a constant parameter with an lvalue (for example, in the argument list of a function with the t&& instead of the const t&) template type deduction will the result change?

Regardless of which faction you belong to (both are reasonable), the tool you want to use depends on what stage of software development you want to know about the results that the compiler derives. We'll explain 3 possible ways to get the inferred type when editing the code, the type to derive at compile time, and the type to derive at run time.

IDE Editor

When you edit the code in the IDE, the compiler displays their type when you hover over the program entities (such as variables, parameters, functions, and so on). For example, in the following code,

constint42 ;auto x = theAnswer;auto y = &theAnswer;

The IDE editor will most likely show that the type of x is int,y type is const int*.

For this work, your code cannot be overly complex, because it is the C + + compiler inside the IDE that provides this information to the IDE. If the compiler does not fully understand and parse your code, producing the result of the type deduction, it will not be able to show you the result of the type deduction.

Compiler diagnostics

A valid way to learn that the compiler is deriving a result from a type is to let it generate a compile-time error. Because the wrong report information will certainly refer to the type that caused the error.

Suppose we want to know the type of x and y in the previous code that were pushed out of the export. We declare a class template first, but do not define it, and the code will look like this:

template<typename T>                   //declaration only for TDclass TD;                              //TD == "Type Displayer"

Attempting to instantiate this template generates an error message because there is no template definition and an instance. To see the types of x and Y, you simply instantiate the TD with their type:

TD<decltype(x)> xType                 //elicit errors containingTD<decltype(y)> yType                 //x‘s and y‘s types;                                      //see Item 3 for decltype info

I use this form of variable name: Variablenametype, because: they tend to produce enough useful error messages. For the above code, one of the compiler's error diagnostic information is as follows (I highlighted the type derivation result we want)

erroraggregate‘TD<int> xType‘ has incomplete type and erroraggregate‘TD<const int *>yType‘ has incomplete type and cannot be defined

The other compiler provides the same information, but the format differs:

error:‘xType‘class ‘TD<int>‘ error:‘yType‘class ‘TD<const int *>‘

Putting the differences in the format aside, all the compilers I've tested provide information that includes useful types of error diagnostics.

Output during the run

Using the printf method (not that I recommend you to use printf) the display type information cannot be used during the program runtime, but it requires full control over the output format. The difficulty is how to make the type of the variable behave reasonably in the form of text, and you may feel "no problem" typeid and std::type_info::name will solve the problem. You think we can write down the following code to know the types of x and Y:

std::couttypeid‘\n‘;     // display types forstd::couttypeid‘\n‘;     // x and y

This method relies on the fact that the return type is std::type_info when typeID acts on an object, and Type_info has a member function called name, which provides a C-style string (for example, const char*) to represent the name of the type.

Calling Std::type_info's name does not guarantee that what is returned must be clear, but will help as much as possible. Different compilers provide different degrees of difference, for example: the GNU and clang compilers represent the type of x as "I", the type of y as "PKI", and once you understand I means that int,pk means pointer to Konst const (two compilers provide a C + + Filt tool to decode these restructured names), understanding the compiler's output will become easier, and Microsoft's compiler provides a clearer output, the type of x is int,y type is int const*.

Because the results for the X and y display are correct, you may think that the problem has been solved, but we cannot be hasty. Consider the more complex example below:

template<typename T>             // template function tovoid f(const T& param);          // be called std::vector<Widget>// factory function constauto vw = createVec();     // init vw w/factory returnif (!vw.empty()) { f(&vw[0]);                       // call f}

This code is more representative when you want to know what type the compiler deduced, because it involves a user-defined type widget, an STD container std::vector, an auto variable, for example, you might want to know the type of the template parameter T, and the type of the function parameter F.

Using typeid looks like a very straightforward approach, just add some code to the type you want to know in F:

template<typenamevoid f(const T& param) {     usingstd::cout;     cout"T = "typeid‘\n‘;         // show T     cout"param = "typeid‘\n‘// show param‘s type...  }

The results of the GNU and Clang implementation are as follows:

T = PK6Widget param = PK6Widget

We already know that PK means pointer to const, and 6 represents how many letters (widgets) are in the name of the class, so the two compilers tell us that the types of T and Param are both const widget*

Microsoft's compilers provide the following results

Tclass Widget const * class Widget const *

These three compilers all provide the same information, which may imply that the results should be accurate, but let's look a little more nuanced, in template F, the type of param is declared as constt&, so is it not surprising that Param and T are the same type? If the type of T is int,param the type should be const int& look, it's not the same.

Sadly, the results of std::type_info::name are not to be relied upon, and in this case, the results of the three compilers are incorrect for the Param, and they must be wrong, because the standard (specification) rule is std::type_ The type of info::name processing is treated according to the value passed to the template, as explained in clause 1, which means that if the type itself is a reference, the reference part is ignored, and if the reference is removed and there is a const, the constants are ignored, which is why the const The type of widget* const & is shown as const widget*, first the reference part of the type is ignored, and then the constants of the result are ignored.

Equally sad is that the type information provided by the IDE is also unreliable, or not so practical, for this example, the compiler I know shows the type of T as (this is not what I invented):

std::_Simple_types<std::_Wrap_alloc<std::_Vec_base_types<Widgetstd::allocator<Widget> >::_Alloc>::value_type>::value_type *

Display the type of param as:

const std::_Simple_types<...>::value_type *const &

This show is not as scary as T, in the middle of ... Just means the IDE tells you that I'm going to show the type of T with ... instead of.

My understanding is that most of what is shown here is caused by a TypeDef, and once you get the potential type information through a typedef, you get what you're looking for, but you need to do some work to eliminate some of the types that the IDE initially shows, and, fortunately, Your IDE editor will handle this code better.

In my experience, using the compiler's error diagnostic information to know the type of the variable being pushed out is a relatively reliable method, using the revised function template F to instantiate just the declared template TD, the revised F looks like this

template<typenamevoid f(const T& param) {     TD<T> TType;                   // elicit errors containing     TD<decltype// T‘s and param‘s types     … }

Gnu,clang and Microsoft compilers provide error messages with the correct type of T and Param, with different formats displayed, such as in the gun (the format is slightly modified)

error‘TD<const Widget *> TType‘typeerror‘TD<const Widget * const &> paramType‘type

Except typeID.

If you want to get a more correct derivation type at run time, we already know that typeid is not a reliable method, a feasible way is to implement a set of mechanisms to complete the mapping from a type to its representation, conceptually it is not difficult, you only need to use the type Trait and template metaprogramming methods to split a full type (using type trait such as Std::is_const,std::is_ponter,std::is_lvalue_reference), You also need to complete the string representation of each part of the type yourself (although you still need typeid and std::type_info::name to produce a string representation of the user-defined format).

If you often need to use this method and think that the effort spent on debugging, documentation, maintenance is worthwhile, then this is a reasonable way (if you're using such a facility often enough to justify the effort needed t o Write, debug,document, and maintain it, that's a reasonable approach), but if you prefer those that are not very strong but can easily be implemented and provide better results than typeid, You need to notice that many compilers provide language extensions to produce a string representation of a function signature, including the types of functions, templates, and template parameters instantiated from the template.

For example, GNU and Clang both support pretty_function, Microsoft supports Funcsig, they represent a variable (in GNU and clang) or a macro (in Microsoft), If we're going to do that with template F,

template<typenamevoid f(const T& param) {#if defined(__GNUC__)                          //For GNU and     std::cout‘\n‘;  // Clang #elif defined(_MSC_VER)     std::cout‘\n‘;          //For Microsoft #endif … }

Call F like before,

std::vector<Widget> createVec();  // factory function constauto vw = createVec();      // init vw w/factory returnif (!vw.empty()) { f(&vw[0]);                        //call f ...}

In GNU we have the following results

void f(const T&) [withconst Widget*]

Tell us that the type of T is deduced as a const widget* (as with the result we get with typeid, but there is no previous PK encoding and 6 in front of the class name), and it also tells us that the F parameter type is const T& if we extend T in this format, The type we get F is the const Widget * const&, and the answer to typeID is different, but the type information in the error diagnostic information that is generated by using an undefined template is consistent, so it is correct.

Microsoft's Funcsig provides the following output:

void __cdecl f<const classWidget*>(constclass Widget *const &)

The type in the angle brackets is the type of T being deduced, for the const widget*. The same results we get with typeid. The type in parentheses is the type of the function parameter, which is the const widget* Const&, and the result we get with typeid is not the same, but it is also consistent with the type information we get at compile time using TD.

Clang's pretty_function, although using the same name as GNU, is not the same format as GNU or Microsoft:

void f(const Widget *const &)

It shows the type of the parameter directly, but we need to deduce that the type of T is deduced for the const widget* (or we can get the type of T by typeID)

The IDE editor, compiler Error diagnostic information, typeid and pretty_function,funcsig language extensions just help you figure out what the compiler deduced. But finally, there is nothing to replace the derivation rules for the type deduction described in clause 1-3.

Please remember:

? You can view type inference by using the IDE compiler, compile error messages, language extensions such as typeID,pretty_function , and Funcsig .

Some tools provide type derivation results that may be neither useful nor accurate, so it is necessary to understand the principles of C + + type derivation.

==============================================================
Translator notes:
The IDE, integrated development Environment, is an acronym for "Integrated development environment" that can assist in developing application software.

"Effective modern C + +" translation-Clause 4: Learn how to view inferred types

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.