Reading notes effective C + + Item 46 If you want to do type conversions, define non-member functions inside the template

Source: Internet
Author: User
Tags function definition

1. Introduction of the problem--templating of operator*

An implicit type conversion for all parameters is explained in Item 24, where only non-member functions are qualified and a operator* function created for the rational class is used as an instance. It is recommended that you review this example before proceeding, because the discussion of this clause is an extension of it, and we will make some seemingly innocuous changes to the instance of item 24: The same templating of rational and opeartor*:

1Template<typename t>2 classRational {3  Public:4Rational (Constt& numerator =0,//See Item-for-why params5 6 Constt& denominator =1);//is now passed by reference7 8 ConstT numerator ()Const;//See Item on why return9 Ten  One ConstT Denominator ()Const;//values is still passed by value, A...//Item 3 for why they ' re const - }; -Template<typename t> the ConstRational<t>operator*(Constrational<t>&LHS, - Constrational<t>&RHS) -{ ... }

As discussed in item 24, we want to support mixed-mode arithmetic operations, so we want the following code to compile. This should not be a problem because we used the same code in item 24. The only difference is that rational and operator* now become templates:

1 rational<int> Onehalf (12);            // This example was from Item 2 // except Rational is now a template 3 4 rational<int2//  error! won ' t compile

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


But the fact that the above code does not compile, it shows that the templated rational and non-template versions are different in some places, and indeed there are differences. In Item24, the compiler knows what function we are trying to invoke (operator* with two rational arguments), but here, the compiler does not know which function we want to invoke. Instead, they try to confirm what functions are instantiated (or created) from the template operator* . They know they want to instantiate some functions named operator*, which have two parameters of type rational<t>, but for instantiation they must confirm what T is. The problem is that they can't know .

To deduce the type of T, they see the type of argument passed when the operator* is called. In the above example, the two parameter types are rational<int> (type of onehalf) and int (2 type). Each parameter is considered separately.

Using Onehalf for deduction (deduction) is easy, operator* the first parameter requires a type of 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 so simple, and the second parameter of operator* requires a type of rational<t>, but the second argument passed to operator* is an int value. In this case, how does the compiler confirm that T is? You might expect them to use Rational<int> 's non-display constructors to convert 2 to a rational<int>, which allows them to interpret t as int, but they do not. because implicit type conversion functions are never considered during the deduction of template parameters . Such conversions are used during function calls, so before you call a function, you must know which function is present. To know this, you have to deduce the parameter type for the associated function template (and then you can instantiate the appropriate function.) However, implicit conversions are not made by calling constructors during template parameter deduction . Item 24 does not involve templates, so the deduction of template parameters is not an issue. Now that we are discussing the C + + Template section (Item1), this becomes the main problem.

3. Problem solving-using friend function 3.1 to declare a friend function in a class template--compile through

We can use the following facts to mitigate the challenge that the compiler accepts for template parameter deduction: a friend declaration in a template class can reference an instantiated function . This means that class ration<t> can declare a operator* of a friend function for ration<t>. The class template no longer relies on template parameter deduction (this process applies only to function templates), so T is always recognized when the class ration<t> is instantiated. So declaring an appropriate friend operator* function can simplify the whole problem:

1Template<typename t>2 classRational {3  Public:4 ...5 6Friend//Declare operator*7 ConstRationaloperator*(Constrational& LHS,//function ( see8 Constrational& RHS);//below for details)9 };Ten  OneTemplate<typename t>//define operator* A  - ConstRational<t>operator*(Constrational<t>& LHS,//functions -  the  - Constrational<t>&RHS) -{ ... }

Now our call to the mixed mode of operator* can be compiled because,ratinonal<t> is instantiated when the object onehalf is declared as a type rational<int> rational<int As part of this process, the friend function operator* with parameter rational<int> is automatically declared. as a declared function (not a function template), the compiler can invoke it using an implicit type conversion function (like rational's non-display constructor), which is how the mixed-mode invocation succeeds .

3.2 Sketch symbols for template and template parameters

Although the code can be compiled, it cannot be linked successfully. We'll deal with it later, but the first thing I want to talk about in the syntax above is declaring operator* in rational.

In a class template, the name of the template can be used as a sketch symbol for templates and template parameters , so in rational<t> we can write rational instead of rational<t>. In this example, only a few characters are reduced for our input, but if there are multiple parameters or longer parameter names, it can reduce the input and make the code look clearer. I'm proposing this because in the example above, operator* 's declaration uses rational as the parameter and return value instead of rational<t>. The following declaration effect is the same:

1Template<typename t>2 classRational {3  Public:4 ...5 friend6 ConstRational<t>operator*(Constrational<t>&LHS,7 Constrational<t>&RHS);8 ...9};

However, the use of sketched forms is easier (more popular).

3.3 Merging the definition of a friend function into a declaration--the link is passed

Now let's go back to the link problem. Mixed-mode code can be compiled, because the compiler knows that we want to invoke an instantiation function (the operator* function with two rational<int> arguments), but this function is only declared within rational, not defined. Our intention is to provide a definition for the operator* template outside the class, but the compiler does not work in this way. If we declare a function ourselves (this is what we Rational within the template), it is also our responsibility to define this function . In the above example, we did not provide a definition, which is why the connector does not know the reason for the function definition.

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

1Template<typename t>2 classRational {3  Public:4 ...5FriendConstRationaloperator*(Constrational& LHS,Constrational&RHS)6 {7 returnRational (Lhs.numerator () * Rhs.numerator (),//same Impl8Lhs.denominator () * Rhs.denominator ());// as in9}//ItemTen }; One  A  

It is true that this works: Mixed mode calls to operator*, compile, link, run all without problems.

3.4 The use of the friend function is very interesting.

The interesting thing about this technique is that the friend function is not used to access the non-public part of the class . In order to make the type conversion possible between all parameters, we need a non-member function (Item 24 still applies here), and in order for the appropriate function to be instantiated automatically, we need to declare a function inside the class. The only way to declare a non-member function inside a class is to declare it as a friend function. That's what we do, it's not customary? Yes. Does it work? No doubt.

4. Discussion on the inline of template friend function

As explained in item 30, functions defined inside a class are implicitly declared as inline functions, which also contain friend functions such as operator*. You can minimize the impact of this inline declaration by letting operator* only invoke a helper function that is defined outside the class body. This is not necessary in the case of this article, since operator* has been implemented into a single line of functions, and for more complex function bodies, helper is probably what you want. The "Let friend function calls helper" method is worth a look.

The fact that rationl is a template means that the helper function is usually also a template, so the code that defines rational in the header file will look like this:

1Template<typename t>classRational;//Declare2 //Rational3 //Template4 5Template<typename t>//Declare6 ConstRational<t> domultiply (Constrational<t>& LHS,//Helper7 8 Constrational<t>& RHS);//Template9 TenTemplate<typename t> One classRational { A  Public: - ... - friend the ConstRational<t>operator*(Constrational<t>&LHS, - Constrational<t>& RHS)//Have friend -  -   +  -{returnDomultiply (LHS, RHS); }//Call Helper +  A ...                                                             at  -};

Many compilers fundamentally force you to put all the template definitions in the header file, so you may also need to define domultiply in your header file. (as Item30 explains, such templates do not require inline). This will look like this:

 template<typename t>  define  const  rational<t> domultiply ( Const  rational<t>& LHS, //  Helper  const  rational<t>& RHS) //  template in  {//  header file,  return  Rational<t> (Lhs.numerator () * Rhs.numerator (), //  If necessary  Lhs.denominator () * Rhs.denominator ())}  

Of course, as a template, domultiply does not support mixed-mode multiplication, but it does not require support. It is only called by operator*, operator* support Mixed mode operation is enough! Fundamentally, the function operator* supports the necessary type conversions to ensure that two rational objects are multiplied, and then it passes the two objects to the appropriate instance of the Domultiply template for the actual multiplication operation. Act together, don't you?

5. Summary

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

Reading notes effective C + + Item 46 If you want to do type conversions, define non-member functions inside the template

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.