Effective C ++, 3rd edition, item 46: defines non-member Fu in the templates template when type conversions is required

Source: Internet
Author: User

Item 46: non-member functions (non-member functions) are defined in the templates (Template) When type conversions is required)

By Scott Meyers

Translator: fatalerror99 (itepub's nirvana)

Release: http://blog.csdn.net/fatalerror99/

Item 24 explains why only non-member functions (non-member functions) are suitable for implicit type conversions (implicit type conversion) applied to all arguments (real parameters ), it also serves as an example to use the operator * function of a rational class. I suggest you familiarize yourself with the example before proceeding, because this item has been extended for a seemingly harmless change (templated rational and operator *) to the example in item 24:

Template <typename T>
Class rational {
Public:
Rational (Const T &Numerator = 0, // see item 20 for why Params
Const T &Denominator = 1); // are now passed by reference

Const tNumerator () const; // see item 28 for why return
Const tDenominator () const; // values are still passed by value,
... // Item 3 for why they're const
};

Template <typename T>
Const rational <T> Operator * (const rational <T> & LHS,
Const rational <T> & RHs)
{...}

Just like in item 24, I want to support mixed-mode arithmetic (mixed mode operation), so we need to compile the following code. We expect it to work, because we use the same code as the code that can work in item 24. The only difference is that rational and operator * are now templates (templates ):

Rational <Int> Onehalf (1, 2); // This example is from item 24,
// Snapshot t rational is now a template

Rational <Int> Result = onehalf * 2; // error! Won't compile

The fact that compilation fails implies that some things are different from non-template versions for templated rational, and they do exist. In item 24, the compiler knows what function we want to call (get the operator * of two rdationals), but here, the compiler does not know which function we want to call. As an alternative, they tryFigure out(Inferred) the function to be instantiated (that is, created) from the template named operator. They know that they assume that a function named operator * of the Instance gets two rational <t> type parameters, but to do this instantiation, they must figure out (inferred) What T is. The problem is that they cannot.

During the deduction t attempt, they will view the type of arguments (real parameter) called by the passed operator. In the current situation, the types are rational <int> (onehalf type) and INT (2 type ). Each parameter is investigated separately.

Using onehalf is simple. The first parameter (parameter) of operator * is declared as the rational <t> type, and the first argument (real parameter) (onehalf) passed into operator * is the rational <int> type, so T must be Int. Unfortunately, deduction for other parameters is not that easy. The second parameter (parameter) of operator * is declared as the int type of rational <t> type, but the second argument (real parameter) (2) of operator * is passed in. In this case, how can the compiler figure out (infer) t? You may expect them to use the non-explicit Constructor (non-explicit constructor) of rational <int> to convert 2 to a rational <int>, in this way, the T is int, but they do not. They do not do this because implicit type conversion functions (implicit type conversion function) is not considered during the template argument deduction (real parameter deduction of the template) process ). Never. This type of conversion can be used in the function call process. This is correct, but you must know which function exists before you can call a function. To understand this, you must push the parameter types (parameter type) for the relevant function templates (function template) (so that you can create an appropriate function for your instance ). However, implicit type conversion called by Constructor (constructor) is not considered during template argument deduction (real parameter deduction of template) (implicit type conversion ). Item 24 does not include templates (Template). Therefore, the template argument deduction (Real-Time Parameter deduction of the template) is not a problem. Now, in the template section of C ++ (see item 1 ), this is the main problem.

In a template class (Template Class), a friend Declaration (youyuan Declaration) can refer to a specific function. We can use this to actually get the template argument deduction (real parameter deduction of the template) the challenging compiler. This means that the class rational <t> can be declared as a friend function operator * for rational <t> *. Class templates (class templates) do not rely on the template argument deduction (template Real-Time Parameter deduction) (this process is only applicable to function templates (function templates )), therefore, t is always known when class rational <t> is instantiated. Make it easy by declaring the appropriate operator * as a friend of rational <t> class:

Template <typename T>
Class rational {
Public:
...
Friend// Declare operator *
Const rational operator * (const rational & LHS,// Function (see
Const rational & RHs );// Below for details)
};

Template <typename T> // define operator *
Const rational <t> operator * (const rational <t> & LHS, // Functions
Const rational <t> & RHs)
{...}

Now we can compile the mixed mode call for operator *, because when the object onehalf is declared as the rational <int> type, class rational <int> is instantiated, as part of this process, the friend function operator * that obtains the rational <int> parameters (form parameter) is automatically declared. As declaredFunction(Function) (not a functionTemplate(Function template). When calling it, the compiler can use implicit conversion functions (implicit conversion function) (for example, rational non-explicit Constructor (non-explicit constructor )), this is how they make the mixed mode call successful.

Alas, in the context, "success" is a silly word, because although the code can be compiled, it cannot be connected. But we will deal with it later. First, I would like to discuss the syntax used to declare operator * in rational.

Within a class template, the name of the template can be abbreviated as template and parameters. Therefore, within rational <t>, we can only write rational instead of rational <t>. In this example, this only saves us a few characters, but when there are multiple parameters or longer parameter names, this not only saves the number of keys but also makes the final code clearer. I mentioned this in advance because operator * is declared as retrieving and returning rationals, rather than rational <t> S. It is as legal as declaring operator * as follows:

Template <typename T>
Class rational {
Public:
...
Friend
Const rational <T> Operator * (const rational <T> & LHS,
Const rational <T> & RHs );
...
};

However, using abbreviations is simpler (and more commonly used ).

Now the connection problem is returned. Mixed Mode code compilation, because the compiler knows that we want to call a specific function (get a rational <int> and a rational <int> operator *), but that function is only within rationalDeclared(Declared), but not hereDefined(Defined ). We intend to allow Operator * template (Template) outside the class to provide this definition, but this method cannot work. If we declare a function (this is what we do inside the rational template), we have the responsibility to define this function. The current situation is that we have not provided a definition, which is why the connector cannot find it.

Perhaps the simplest way to make it work is to merge the operator * ontology into its declaration (Definition:

Template <typename T>
Class rational {
Public:
...

Friend const rational operator * (const rational & LHS, const rational & RHs)
{
Return rational (LHS. numerator () * RHS. numerator (),// Same impl
LHS. denominator () * RHS. denominator ());// As in
}// Item 24
};

Indeed, this can work as expected: the mixed mode call to operator * can now be compiled, connected, and run. Long live!

An interesting observation of this technology is that the use of friendship does not play a role in the non-public parts (non-public component) access to the class. To make the type conversions (type conversion) of all arguments (real parameters) possible, we need a non-member function (non-member function) (item 24 still applies ); in order to automatically create an appropriate function, we need to declare this function within the class. The only way to declare a non-member function (non-member function) within a class is to make it a friend ). So this is what we do. Is it against tradition? Yes. Valid? No doubt.

As described in item 30, functions defined in a class are implicitly declared as inline (Inline), which also includes friend functions such as operator ). You can let operator * do nothing, just call a helper function defined outside the class, so as to minimize the impact of such inline declarations (Inline Declaration. In this example of this item, this is not particularly pointed out, because operator * can already be implemented as a one-line function (single-line function), but for more complex function bodies, this may be appropriate. The "have the friend call a helper" ("let friends call helper functions") method is worth noting.

The fact that rational is a template means that the Helper function (helper function) is usually also a template, so the code for defining rational in the header file typically looks roughly as follows:

Template <typename T> class rational;// Declare
// Rational
// Template
Template <typename T>// Declare
Const rational <t> domultiply (const rational <t> & LHS,// Helper
Const rational <t> & RHs );// Template
Template <typename T>
Class rational {
Public:
...

Friend
Const rational <t> operator * (const rational <t> & LHS,
Const rational <t> & RHs) // have friend
{Return domultiply (LHS, RHS );}// Call helper
...
};

Most compilers force you to put all template definitions in the header file. Therefore, you may need to define domultiply in your header file. (As described in item 30, such templates (templates) do not require inline (Inline ).) It may 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. It is called only by operator *, while operator * supports mixed mode operations! Essentially,FunctionOperator * supports various type conversions (type conversion) required to ensure that two rational objects are multiplied, and then it passes the two objects to a domultiplyTemplate(Template) Appropriate instantiation for actual multiplication. Cooperation, isn't it?

Things to remember

  • When writing a class template, this class template provides some functions that support all parameters (parameters) when implicit type conversions (implicit type conversion) templates, these functions are defined as friends (youyuan) inside 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.