"Effective C + +": Clause 41-clause 42

Source: Internet
Author: User
Tags traits

    • Article 41 Understanding implicit interfaces and compile-time polymorphism
    • Article 42 understanding the dual meaning of TypeName

Article 41: Understanding implicit interfaces and compile-time polymorphism

Object-oriented programming always solves the problem with display interface (explicit interfaces) and run-time polymorphism (runtime polymorphism). For example

 class Widgets{public:Widgets(); Virtual ~Widgets(); Virtual std::size_t Size()Const virtual void normalize(); void swap(widgets& Other);    ...... }; void doprocessing(Widgets& w){if(w.  Size()>10 && w!=somenasywidget) {WidgetsTemp(w); Temp.normalize(); Temp.swap(w); }    }

Can say that doProcessing within the W

    • The type of W is declared as a widget, so w must support the Widget interface.
    • Some member functions of the widget are virtual,w that the call to such a function will show runtime polymorphism (runtime polymorphism).

In the world of templates and generics programming, the display interface and the run-time polymorphism still exist, but more to the implicit interface (implicit interface) and compiler polymorphism (Compile-time polymorphism). Change doProcessing from function to function template

template<typename T>    doProcessing(T& w)    {        if(w.size()>10 && w!=someNasyWidget)        {            temp(w);            temp.normalize();            temp.swap(w);        }    }

Now look doProcessing inside the W

    • W must support which interface, which is determined by the operation performed on W on the template.
    • Any function calls involving W, such as operator> and operator!=, may cause the template to be instantiated (instantiated), making these calls successful. This is happening at compile time. Using the existing function template with different template parameters results in different functions being called, which is the compile-time polymorphism (Compile-time polymorphism).

Compile-time polymorphism and run-time polymorphism are not difficult to distinguish, now look at the difference between explicit interface and implicit interface. The explicit interface usually has a function signature (function name, parameter type, return value).

    class Widget{    public:        Widget();        virtual ~Widget();        virtualconst;        virtualvoid normalize();        void swap(Widget& other);        ……    };

The public interface consists of a constructor, a destructor, a function size,normalize, a swap, and its arguments, return values, constants (constness), including the copy constructor generated by the compiler and the copy assignment operator.

The implicit interface is completely different, it is not determined by the function signature, but is composed of a valid expression (valid expression).

template<typename T>    doProcessing(T& w)    {        if(w.size()>10 && w!=someNasyWidget)        {            temp(w);            temp.normalize();            temp.swap(w);        }    }

It can be seen that the implicit interface of the T (w type) seems to have these constraints

    • You must provide a function called size that returns an integer value
    • Must support a operator! = Sweat or, used to compare two objects.

In fact, these two constraints must not be fulfilled. T must support the size member function, but this function may inherit from base class. This function does not need to return an integer value and does not even need to return a numeric type. You don't even need to return a type that defines a operator>. The only thing it has to do is return an object of type X, and the X object plus an int (10 type) must be able to invoke a operator>. This operator> does not need to get a parameter of type X, it can take a parameter of type Y, as long as there is an implicit conversion to convert the object of type X into an object of type Y. Likewise, T requires support for operator!=.

The above analysis has not considered operator&& is overloaded, a connection word change may be completely different from something, may change the meaning of the above expression.

The first time you think of an implicit interface in this way, you feel unaccustomed. Implicit interfaces consist only of a set of valid expressions that may seem complex, but the constraints they require are generally fairly straightforward and unambiguous, such as:

if(w.size()>10 && w!=someNasyWidget)

It's hard to say too much about the constraints on the function size, operator>, operator&&, operator!=, but it's easy to confirm the expression constraints as a whole. The IF expression must be a Boolean value, so the overall expression must be bool-compatible. At this point in the template doprocessing part of the implicit interface of type parameter T, doprocessing requires other implicit interfaces: the copy constructor, normalize, and swap must also be valid for T-type objects.

The implicit interface that is added to the template parameter is as real as the interface of the class object, which is checked at compile time, and if the implicit interface required by template is not supported in the template, the code cannot be compiled by

Summarize

    • Both class and template support interfaces and polymorphism.
    • The interface of class is explicit, with function signature as the center, and polymorphism through virtual function at run time.
    • The interface of the template is implicit, based on a valid expression. Polymorphism is done by template materialization and function overloading parsing (function overloading resolution) occurring at compile time.
Article 42: Understanding the dual meaning of TypeName

When using a template, you can use TypeName, or you can use the class

template<classclass Widget;    template<typenameclass Widget;

There's no difference between the two. As the type parameter of the template, the meaning is exactly the same. In terms of usage, many people prefer to use TypeName because this implies that the parameter is not necessarily a class type.

C + + Sometimes does not equate class with TypeName. Sometimes it is necessary to use TypeName; In order to understand the principle, we first talk about the two names of the referent (refer to) within the template.

With an example, there is now a template function that receives an STL-compliant container as a parameter, and the object in the container can be assigned an int, which prints the value of the second element

template<typename C>    void print2nd(const C& container)    {        if(container.size()>=2)        {            C::const_iterator iter(container.begin());            ++iter;            int valaue=*iter;            std::cout<<value;        }    }

Now explain the two local variables iterate and value. The iterate type is c::const_iterator, actually what pleases the template parameter C. The name that appears in the template, if dependent on a template parameter, is called a subordinate name (dependent names). If the subordinate name is nested within class, it is called a nested subordinate name (nested dependent name). C::const_iterator is such a nested subordinate name (nested dependent name) and refers to a type. value is of type int and is not dependent on the template parameter, which is called a non-subordinate name (non-dependent names).

Nested subordinate names can cause parsing (parsing) difficulties, such as doing so in PRINT2DN

template<typename C>void print2nd(const C& container){    C::const_iterator* x;    ……}

On the surface it is declared that a local variable x,x type is just a c::const_iterator pointer, because we are until c::const_iterator is a type. What if it's not? For example, a static member variable in C happens to be named Const_iterator, or X is a global variable, which is a multiplication action: const::const_iterator times x. The person writing the C + + parser must worry about such input.

It is not possible to determine whether c::cont_iterator is a type until after C is. When the compiler parses the template print2nd, I don't know what C is. C + + has a rule that resolves this ambiguous state: if the parser encounters a nested subordinate name in the template, it assumes that this understanding is not a type unless you tell it to. So, by default, nested subordinate names are not types. There is an exception to this rule, and we will mention it later.

Looking at print2nd, C::const_iterator iter C::const_iterator It is only reasonable when it is a type, but the C + + default thinks it is not. To rectify this form, we must tell C + + to C::const_iterator be a type and place the keyword typename in front of it.

template<typename C>    void print2nd(const C& container)    {        if(container.size()>=2)        {            typename C::const_iterator iter(container.begin());            ++iter;            int valaue=*iter;            std::cout<<value;        }    }

The exception to the rule "TypeName must be prefixed with the name of a nested subordinate type" is that TypeName cannot appear in the base class list until the nested subordinate type name, nor can it be member initialization List (Member initial column) as the base class modifier.

template<typename T>    classpublic//base class list中不允许typename    public:        explicit Derived(int x)        :Base<T>::Nested(x)  //mem init.list中不允许typename        {            typename//嵌套从属名称类型            ……  //既不在base class list中,也不再mem init list中,作为base class修饰符要加上typename        }        ……    };

Let's look at a typename example. A function template accepts an iterator, and we intend to make a local copy (replica) temp for the object that the iterator refers to

    IterT>    void workWithIterator(IterT iter)    {        typename::std::iterator_traits<IterT>::value_type temp(*iter);        ……    }

typename::std::iterator_traits<IterT>::value_typeis an application of the standard traits class (* * clause **47). std::iterator_traits<IterT>::value_typeis a nested subordinate class name, so TypeName is placed in front of it. If the use std::iterator_traits<IterT>::value_type feels too long to be used, consider establishing a TypeDef, for traits member names, the general habit is to set the TypeDef name to represent a traits member name.

IterT>    void workWithIterator(IterT iter)    {        typename::std::iterator_traits<IterT>::value_type value_type;        value_type temp(*iter);        ……    }

Finally, TypeName related rules have different practices on different compilers. This means that the interaction between TypeName and nested subordinate names may cause headaches in terms of portability.

Summarize

    • When declaring the template parameter, the Prefix keyword class and typename are interchangeable.
    • Use the keyword typename to identify nested subordinate type names, but not as base class modifiers in the base class list or member initialization list (Member initial column).

"Effective C + +": Clause 41-clause 42

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.