"Effective C + +": Clause 46-Clause 47

Source: Internet
Author: User
Tags mixed traits

    • Clause 46 When you enter conversions, you need to define non-template member functions
    • Clause 47 Please use the Traits class expression type information

Clause 46: Defining a non-member function for a template when a type conversion is required

clause 24 mentions why the Non-member function has the ability to "implement implicit type conversions on all the implementations." This article goes on to that rational example. Templating the Rational class

    Template<TypeNameT>classrational{ Public: Rational (Constt& numerator=0,Constt& denominator=1);ConstT numerator ()Const;ConstT Denominator ()Const; ...... };Template<TypeNameT>ConstRational<t>operator*(Constrational<t>& LHS,Constrational<t>& rhs) {...}; rational<int> Onehalf (1,2); rational<int> result=onehalf*2;//error, failed to compile

Examples of non-templates can be compiled. But the templating sample is not. In the clause **24, the compiler does not know what function we are trying to invoke (that is, accepting two rational parameters that operator ). The compiler is trying to figure out what functions are named operato*, and they know that they can detail a operator* and accept two rational parameters. But to complete this detailed action, you must first figure out what T is. The problem is they don't have the ability.

Take a look at this example. How the compiler deduces T.

The type parameters in this example are rational and int respectively. The first parameter of the operator* is declared rational. The first real participation (ONEHALF) positive type passed to operator* is rational. So t must be int. The second parameter type of operator* is declared as rational. But the second type of argument passed to operator* is int, how does the compiler extrapolate T? You might expect the compiler to use rational's non-explicit constructor to convert 2 to rational, and then deduce that T is int, but it does not, because implicit type conversions are never taken into account in the template-argument derivation process.

Implicit conversions are actually used during function calls, but before you can invoke a function, you first need to know the existence of that function. In order to know the existence, you must first derive the parameter type for the relevant function template (and then be able to detail the appropriate functions).

However, implicit type conversions that occur through constructors are not considered during the derivation of the template argument.

Now solves the challenge of the compiler in the template implementation, the ability to use the friend function within the template class. Because friend declarations within the template class can refer to a specific function. In other words, class rational can demonstrate that operator* is its friend function. The class templates does not rely on the template practice derivation (the latter is only performed on function templates), so the compiler is always able to know t when the class rational is detailed.

So it is possible to simplify the whole problem by making rational class declare the appropriate operator* as a friend function.

 template  <typename  t> class  rational{        Public : ... friend  const  Rational operator  * (const  rational& lhs,< Span class= "Hljs-keyword" >const  rational& RHS);    //Declaration }; template  <typename  t> const  rational<t> operator  * ( const  rational<t>& lhs,const  rational<t>& RHS)    //definition  {...};  

This time the mixed call to operator* can be compiled. Onehalf is declared as a rational,class rational is detailed. As part of the process, the friend function operator* (accepting rational parameters) also declares itself voluntarily.

The latter is a function rather than a function template, so the compiler uses implicit conversions when calling it (converting int to rational). So mixed calls can be compiled. Although compiled, there will be links to the problem, this later on. Let's take a look at the syntax for declaring operator * within rational.

Within a class template, the template name can be used as a shorthand expression for the template and its parameters, so within rational we can abbreviate rational to rational.

Let's say this is written as follows. As effective

    template<typename T>    class Rational{    public:        ……        friendconstoperator*(const Rational<T>& lhs,const Rational<T>& rhs);//声明    };

Now look back at the problem of the link you just said.

Although the compiler until the function we call is the operator * that accepts rational, this function is only declared, undefined. We wanted to provide a definition for the operator * outside of this class, but that wouldn't work. Assuming that we ourselves declare a function (as in Rational template), it is the responsibility to define that function. Assumptions are undefined. The linker could not find it. One of the simplest ways is to merge the definition of operator * into its declaration:

    template<typename T>    class Rational{    public:        ……        friendconstoperator*(const Rational& lhs,const Rational& rhs);//声明+定义        {            return Rational(lhs.numerator()*rhs.numerator(),lhs.denominator()*rhs.denominator());        }    };

This technique, while using friend, differs from the traditional friend use of "visiting class Non-public members".

In order for the type conversion to take place with all of the actual participation. We need a non-member function (* * clause **24). In order for this function to be self-detailed, we need to declare it inside class, and the only way to declare the Non-member function inside class is to make it a friend.

Functions that are defined inside class are inline functions. Contains the friend function like operator *. To minimize the impact of the inline declaration. Enables operator * To invoke a helper function defined outside the class.

    Template<TypeNameT>classRational;//forward decelarion    Template<TypeNameT>ConstRational<t> domultiply (Constrational<t>& LHS,Constrational<t>& RHS);Template<TypeNameT>classrational{ Public: ......friend ConstRationaloperator*(Constrational& LHS,Constrational& RHS);//declaration + definition{returnDomultiply (LHS,RHS); }    };

Many compilers will force you to put the template definition into the header file, so sometimes you need to define Domultiply

    T>    const Rational<T> doMultiply(const Rational<T>& lhs,const Rational<T>& rhs)    {        return Rational<T>(lhs.numerator()*rhs.numerator(),lhs.denominator()*rhs.denominator());    }

Domultiply is a template that naturally does not support mixed multiplication, and in fact it is not necessary to support it. It is only called by operator *, operator * supports mixed multiplication.

Summarize

    • When writing a class template, which provides the function associated with this template to support the implicit type conversion of all parameters, define those functions as a friend function inside the class template.

Article 47: Please use traits class performance type information

STL is mainly composed of templates of container, iterator and algorithm, and also contains some instrumental templates. One of the advance is used to move the iterator to a given distance:

    template<typenametypename DistT>    void advance(IterT& iter, DistT d);//d大于零。向前移动,小于零则向后移动

On the surface, it is only the Iterate+=d action, but the iterator has 5. The + = operation is only supported by the random access (randomly interviewed) iterator. Other types are not so powerful. Just repeat + + + and – only.

The parts of the STL source code about iterators can be tested here. Here's a look back at the 5 iterators.

    • The input iterator. It is read only, it can only read the object it points to, and can only read it once. It can only move forward. One step at a time.

      It mimics the reading pointer to the input file (read pointer); The istream_iterators in the C + + program is the representative of this class.

    • The output iterator, as opposed to the input iterator. It is write only.

      It is also only able to move forward, one step at a time. And can only scribble the object it points to once. It mimics a scribble pointer to the output file (write pointer); Ostream_iterators is a representative of this category.

    • Forward iterators. This iterator derives from the input iterator, so there is full functionality for the input iterator. And he was able to read and write to the object more than once.
    • The bidirectional iterator inherits from the forward iterator, and its functionality also includes moving backwards.

      The list, set, Multiset, map, and Multimap iterators in the STL are this type of iterator.

    • The random access iterator inherits from the bidirectional iterator. The great thing about it is the ability to jump forward or backward at random distances, which is similar to the original pointer, and the built-in pointer can be used as a random access iterator. The iterator for vectors, deque, and string is this class.

These are categorized in 5. The C + + standard library provides a proprietary volume label structure (tag struct) to confirm:

    struct input_iterator_tag {};      struct output_iterator_tag {};      structpublic input_iterator_tag {};      structpublic forward_iterator_tag {};      structpublic bidirectional_iterator_tag {};

After understanding the iterator type, it is time to implement the advance function. Implementation to be efficient. For the random access iterator, the forward D distance is completed in one step. Other types are required to repeat forward or backward.

    template<typenametypename DistT>    void advance(IteT& iter,DistT d)    {        if(iter is a random access iterator)            iter+=d;        else        {            if(d>=0)                while(d--) ++iter;            else                 while(d++) --iter;        }    }

In the above implementation, you want to infer whether ITER is a random access iterator. That is, you know whether the Itert type is a random access type. This requires traits, which agrees that we can obtain certain types of information during compilation. Traits is a technology that is a protocol that C + + program apes abide by.

One of the technical requirements is that it behaves as well as the built-in type and its own definition type. Traits must be able to execute on built-in types. means "Nested information within a type" this thing is out, because we can't nest information inside the value pointer.

So the traits information for the type must be outside the type itself. The standard technique is to put it into a template and one or more of its special version numbers. This templates has several in the STL, the iterator is iterator_traits:

    template<typename IterT>//用来处理迭代器分类    struct iterator_traits;

Although Iterator_traits is a struct, it is often called traits classes. It works by declaring a typedef named Iterator_category in the struct iterator_traits for each type of itert, to confirm the Itert iterator classification. Iterator_traits in two parts to achieve the above mentioned.

1, it requires the user's own defined iterator nested a typedef, named Iterator_category. Used to determine which volume label structure (tag struct), such as deque and list

 Template<TypeNameT>class deque{ Public:classiterator{ Public:typedefRandom_access_iterator_tag iterator_category;        ...... }; ...... };Template<TypeNameT>class List{ Public:classiterator{ Public:typedefBidirectional_iterator_tag iterator_category;        ...... }; ...... };Template<TypeNameItert>//itert's iterator_category is used to show what Itert says he is.    structiterator_traits{use of//typedef TypeName. See * * clause **42        typedef TypeNameItert::iterator_category iterator_category; ...... };

This makes sense for user-defined types, but not for pointers, and pointers to iterators. But pointers cannot nest typedef. The following is the 2nd part of Iterator_traits. Specifically used to support pointers.

To support Pointer iterators. Iterator_traits provides a partial version number (partial template specialization) specifically for the type.

    template<typename IterT>    struct iterator_traits<IterT*>//针对内置指针    {        typedef random_access iterator_tag iterator_category;        ……    };

It is now possible to implement a traits class step

    • Identify certain types of information that we would like to be able to obtain in the future.

      For iterators to come first. is to be able to obtain its classification.

    • Select a name for this information. For iterators is iterator_category.

    • Provides a template and a set of special version numbers. Contains the types and related information that you want to support.

Now it's possible to realize the advance.

    template<typenametypename DistT>    void advance(IterT& iter,DisT d)    {        if(typeid(typenamestd::iterator_traits<IterT>::iterator_category)==        typeid(std::random_access_iterator_tag))        ……    }

Although the logic is correct, it is not what we want. Aside from the compilation issue (* * clause **48), another more fundamental problem: the Itert type is known during compilation. So Iterator_traits::iterator_category is determined during compilation.

However, the IF statement is approved during execution. The ability to be able to complete during compilation is pushed to the execution period, which not only wastes time, but also causes the execution of file bloat.

To be determined during compilation. Ability to use overloads.

Overloads are determined during compilation, and the compiler finds the most matching function to invoke the

    Template<TypeNameItert,TypeNameDist>voidDoadvance (itert& iter, Dist D,STD:: Random_access_iterator_tag) {iter+=d; }Template<TypeNameItert,TypeNameDist>voidDoadvance (itert& iter, Dist D,STD:: Bidirectional_iterator_tag) {if(d>=0) while(d--) ++iter;Else          while(d++)--iter; }Template<TypeNameItert,TypeNameDist>voidDoadvance (itert& iter, Dist D,STD:: Input_iterator_tag) {if(d<0)Throw STD:: Out_of_range ("Negative Distance"); while(d++)--iter; }Template<TypeNameItert,TypeNameDistt>voidAdvance (itert& Iter,distt D) {doadvance (iter,d,TypeName::STD:: Iterator_traits<itert>::iterator_category (); }

Since Forward_iterator_tag inherits from Input_iterator_tag, the Input_iterator_tag version number function can handle forward iterators. This is because public inheritance is a is-a relationship.

Now to summarize how to use the traits class

    • Create a set of overloaded functions or function templates (such as doadvance). The difference between each other is only the respective traits parameters. Each function implements a match with the traits information it receives.
    • Create a control function or function template (such as advance), call the function above and pass the traits class information.

Traits is widely used in STL. In addition to the above mentioned iterator_traits, there are char_traits to hold information about the character type. Numeric_limits is used to hold information about numeric types.

TR1 (* * clause **54 import many new traits classes used to provide type information. For example, if Is_fundamental infers if T is a built-in type, Is_array infers if T is an array. Is_base_of

Copyright notice: This article Bo Master original articles, blogs, without consent may not be reproduced.

"Effective C + +": Clause 46-Clause 47

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.