Reading Notes Objective c ++ Item 46 if you want to perform type conversion, define the non-member function within the template.

Source: Internet
Author: User

Reading Notes Objective c ++ Item 46 if you want to perform type conversion, define the non-member function within the template.
1. Introduction of the problem-templated operator *

Item 24 explains why only non-member functions are eligible for implicit type conversion of all parameters and uses an operator * function created for the Rational class as an instance. Before proceeding, we recommend that you review this example, because this clause is about its extension, and we will make some seemingly harmless changes to the instance of Item 24: templated both Rational and opeartor:

 1 template<typename T> 2 class Rational { 3 public: 4 Rational(const T& numerator = 0, // see Item 20 for why params 5  6 const T& denominator = 1);         // are now passed by reference 7  8 const T numerator() const;          // see Item 28 for why return 9 10 11 const T denominator() const; // values are still passed by value,12 ... // Item 3 for why they’re const13 };14 template<typename T>15 const Rational<T> operator*(const Rational<T>& lhs,16 const Rational<T>& rhs)17 { ... }

 

As discussed in Item 24, we want to support arithmetic operations in mixed mode, so we want to compile the following code. This should be fine, because we use the same code in Item 24. The only difference is that Rational and operator * have now become templates:

1 Rational<int> oneHalf(1, 2);            // this example is from Item 24,2 // except Rational is now a template3 4 Rational<int> result = oneHalf * 2; // error! won’t compile

 

2. Problem Analysis-template parameter deduction cannot be implicitly converted


But in fact, the above Code will not be compiled, which indicates that the templated Rational and non-template versions are still different in some places and are indeed different. In Item24, the compiler knows what function we are trying to call (operator * with two Rational parameters), but here, the compiler does not know which function we want to call. Instead, they try to confirmFrom TemplateOperator *What kind of functions are implemented by examples (that is, created )?. They know that they want to instantiate some functions named operator *. These functions have two types of Rational <T> parameters, but to instantiate them,They must be confirmedTWhat is it. The problem is that they cannot know.

To deduce the T type, they see the parameter type passed when operator * is called. In the preceding example, the two parameter types are Rational <int> (oneHalf type) and int (2 type ). Each parameter is considered separately.

It is easy to use oneHalf for deduction (deduction). The type required for the first parameter of operator * is Rational <T>, in fact, the type of the first parameter passed to operator * is Rational <int>, so T must be int. Unfortunately, the deduction of other parameters is not that simple, and the type required by the second parameter of operator * is also Rational <T>, but the second parameter passed to operator * is an int value. In this case, how does the compiler determine what T is? You may expect them to use the Rational <int> non-display constructor to convert 2 to a Rational <int>, so that they can convert T to int, but they did not.Because implicit type conversion functions are never considered during template parameter deduction.. Such conversions are used during function calls. Therefore, you must know which function exists before calling a function. To understand this, you must deduce the parameter type for the relevant function template (and then you can create an appropriate function for the instance .) However, during template parameter deduction, no implicit conversion is performed by calling the constructor..Item 24 does not involve templates, so the deduction of template parameters is not a problem. Now we are discussing the template part (Item1) of C ++, which becomes the main issue.

 

3. Problem Solving-use youyuan function 3.1 to declare youyuan function in the class template-compile and pass

 

The following facts can be used to alleviate the Challenge accepted by the compiler for the deduction of template parameters:A friend declaration in the template class can reference an instantiated function.. This means that the class Ration <T> can declare an operator * of the friend function for Ration <T> *. The class template no longer depends on the parameter deduction of the template (this process is only applied to the function template), so T can always be confirmed when the class Ration <T> is instantiated. Therefore, declaring a proper friend operator * function can simplify the entire problem:

 1 template<typename T> 2 class Rational { 3 public: 4 ... 5  6 friend // declare operator* 7 const Rational operator*(const Rational& lhs, // function (see 8 const Rational& rhs); // below for details) 9 };10 11 template<typename T>                                                              // define operator*12 13 const Rational<T> operator*(const Rational<T>& lhs, // functions  14 15 16 const Rational<T>& rhs)17 { ... }

 

Now we can compile the call to the mixed mode of operator *, because when the object oneHalf is declared as type Rational <int>, ratinonal <T> is instantiated and called Rational <int>. As part of this process, operator *, which has the parameter Rational <int>, is automatically declared.As a declared function(Not a function template ),The compiler can use an implicit type conversion function when calling it.(Like the Rational non-display constructor ),This is how to make the mixed mode call successful.

3.2 sketch symbols for templates and template parameters

Although the Code can be compiled, it cannot be linked successfully. We will deal with it later, but the first thing I want to discuss with the above syntax is to declare operator * in Rational *.

In a class template,The template name can be used as a sketch symbol for the template and template parameters.Therefore, in Rational <T>, we can write it as Rational instead of Rational <T>. In this example, only a few characters are reduced for our input, but if there are multiple or longer parameter names, it can reduce the input and make the Code look clearer. In the preceding example, operator * uses Rational as the parameter and return value, rather than Rational <T>. The effect of the following statement is the same:

1 template<typename T>2 class Rational {3 public:4 ...5 friend6 const Rational<T> operator*(const Rational<T>& lhs,7 const Rational<T>& rhs);8 ...9 };

 

However, it is easier (more popular) to use the speed writing format ).

3.3 merge the definition of the friend functions into the declaration-link through

Now let's go back to the link. Mixed-mode code can be compiled because the compiler knows that we want to call an instantiated function (operator * function with two Rational <int> parameters ), however, this function is declared only within Rational, not defined. Our intention is to let the operator * template outside the class provide definitions, but the compiler does not work in this way.If we declare a functionRationalTemplate), we also have the responsibility to define this function.. In the above example, we didn't provide a definition, which is why the connector cannot know the function definition.

The simplest possible solution is to merge the operator * function body into the Declaration:

 1 template<typename T> 2 class Rational { 3 public: 4 ... 5 friend const Rational operator*(const Rational& lhs, const Rational& rhs) 6 { 7 return Rational(lhs.numerator() * rhs.numerator(), // same impl 8 lhs.denominator() * rhs.denominator()); // as in 9 } // Item 2410 };11 12  

 

Indeed, this can work: there is no problem in calling, compiling, linking, and running operator * in mixed mode.

3.4 It is very interesting to use a friend function.

What's interesting about this technology is thatThe non-PublicPart. To make the type conversion between all parameters possible, we need a non-member function (Item 24 is still applicable here); and in order to make the appropriate function automatically instantiated, we need to declare a function within the class. The only way to declare a non-member function within a class is to declare it as a friend function. This is what we do, not in line with the Convention? Yes. Valid? Beyond all doubt.

4. Discussion about the template's friend meta function inline

As explained in Item 30, functions defined inside the class are implicitly declared as inline functions, which also include friend functions like operator. You can minimize the impact of such inline declarations by allowing operator * to call only one helper function defined in the class in vitro. There is no need to do this in the example of this clause, because operator * has been implemented as a function with only one row. For more complex function bodies, helper may be what you want. It is worth noting that the method of "Let friend functions call helper.

The fact that Rationl is a template means that the helper function is usually a template, so the code for defining Rational in the header file will look like the following:

 1 template<typename T> class Rational; // declare 2 // Rational 3 // template 4  5 template<typename T> // declare 6 const Rational<T> doMultiply( const Rational<T>& lhs, // helper 7  8 const Rational<T>& rhs);                                         // template 9 10 template<typename T>11 class Rational {12 public:13 ...14 friend15 const Rational<T> operator*(const Rational<T>& lhs,16 const Rational<T>& rhs)                                          // Have friend17 18  19 20 { return doMultiply(lhs, rhs); }                // call helper21 22 ...                                                            23 24 };           

Many compilers force you to place all template definitions in header files, so you may also need to define doMultiply in your header files. (As Item30 explains, such a template does not require inline ). This will look like the following:

template<typename T> // defineconst Rational<T> doMultiply(const Rational<T>& lhs, // helperconst Rational<T>& rhs) // template in{ // header file,return Rational<T>(lhs.numerator() * rhs.numerator(), // if necessarylhs.denominator() * rhs.denominator());}

Of course, as a template, doMultiply does not support multiplication in mixed mode, but does not. It is only called by operator *, and operator * supports mixed mode operations! Basically, the function operator * supports necessary type conversion to ensure that two Rational objects are multiplied, it then passes the two objects to the appropriate instance of the doMultiply template for actual multiplication. Collaborative action, isn't it?

5. Summary

When implementing a class template that provides functions, if all the parameters of these functions support implicit type conversion related to the template, these functions are defined as friend functions within the class template.

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.