CUJ: SUTTER's MILL: Befriending Templates

zhaozj2021-02-16  33

Let template to be a friend of: Herb Sutter Source: CUJ: Sutter's Mill ----------------------------------- ---------------------------------------------

Suppose we have a function template that performs SomethingPrivate () on the object it operates. Special, consider

Boost :: Checked_Delete () Function Template, it delete is transmitted to its object - during this process, it calls the destructor of the object: Namespace Boost {Template Void Checked_Delete (t * x) {//. .. Other stuff ... delete x;}}

Now, suppose you want to put this function template on a class, the problem is, what you need (here is a destructor) happens to be private

: // EXAMPLE 1: No Friends // Class Test {~ TEST () {} // private!};

Test * t = new test; boost :: checked_delete (t); // error: Test's Destructor is private, // so checked_delete can't call it.

The solution is very simple: just let Checked_Delete () become a friend of TEST. (The only other option is to abandon [Package], so that the destructive function becomes public).

I wrote this article for this reason. Hey, let the template in another namespace becomes a friend, saying that it is easy to do: l bad news: There are two fully compliant methods to implement it, but no A work on all compilers currently. l Good news: One of them can work on every current compiler I have tried in addition to GCC.

Original attempt --------------------------------------------- --------------------------------- Here is the original code: Stephan Born Written as: // EXAMPLE 2: One Way to Grant Friendship (?) // Class Test {~ TEST () {} Friend Void Boost :: Checked_Delete (TEST * X);

Hey, this code cannot work on the poster's compiler (VC 6.0). In fact, it doesn't work on many compilers. To be, the friend declaration in Example 2: L technology is legal, but a vague point L in [C ] language is rejected by many current compilers, including some very excellent compiler L very easy to fix it. Depending on the poor point, work on almost all compilers (except GCC)

Why is it legal, but

When the approved friend, four situations will occur (listed in C standard, Clause 14.5.3). The summary is as follows: When the approved friend does not use the "template" key to use "template": 1. If the name of the friend looks like a template-specific version with explicit parameters (for example, Name ) So, friend is a explicit-specific version of that template 2. Secondly, if the name of the friend is defined by the name or NameSpace name (for example, Some :: Name), and, class or namespace contains a match. Non-template functions, then, friend is that function 3. Moreover, if the name of the friend is defined by the name or Namespace name (for example, some :: name), and, class or namespace

Including a matching template function (can derive the right template parameters), then the friend is a special version of the function template 4. Finally, the name must be limited to [class or Namespace name], and declaration (Or repeatedly declared) a normal (non-template) function.

Very clear, # 2 and # 4 only matches non-template, so the special version of the template is declared as a friend, we have two options: write

Meet rule # 1, or write to meet rules # 3. Corresponding to our example, the choice is: // The Original Code, Legal Because It Falls Into Bucket # 3 // Friend Void Boost :: Checked_Delete (Test * x); or // adding "", Legal Because IT Falls Into Bucket # 1 // Friend Void Boost :: Checked_Delete (TEST * X);

The first is the second simple form ... However, only when the name is limited (here "boost ::"), and, there is no match.

The matching non-template function is located in the same scope space. This friend declaration rule is full of distort people - for most current compilation

The same is true! I can find at least three reasons to avoid using it.

Why avoid rules # 3

There are some reasons to avoid rule # 3, even if it is technically legal: 1. Rule # 3 does not work. As mentioned above, it is a short-written form of the initial rule, but only in the class name or Namespace name, and there is no matching non-mode

When the board function is played. In particular, if Namespace has (or later) a matching non-template function, it will change the selection because the non-template function is

The appearance will result in rule # 2 to preempt the rule # 3. Some subtle and surprised, isn't it? It's easy to make mistakes, isn't it? Let us avoid this

Subtle. 2. Rule # 3 is really sharp, fragile, and makes most people who read your code feel surprised. For example, consider this very small variant - all the changes I do is to remove the word "boost": // variant: make the name unqualified // class test {~ test ()} friend void checked_delete (test) * x); // Ouch: Legal, But not what you}; // want. More about this late. If you omit "Boost ::" (that is, if the call is unlimited), you will fall into one Completely different rules (rules # 4)

It does not match the function template at all, which is not fun at all. Gambling 20 yuan, almost our beautiful planet everyone

I will agree with my point of view: It's too surprising, just omitting a Namespace name, and changed the reputation of the friend declaration.

significance. Let us avoid this sharp thing.

Rules # 3 sharp, fragile, pumsed most compiler

Let us use rules # 1 and rules # 3, and make a wide range of trials on the current compiler to see how they understand. Compiler

Standard understanding and we are consistent (after we read it so much)? At least the most cattle compiler meets our expectation? no,

Still (No, And No, Respective).

Let's try the rules first # 3: // example 1 again // namespace boost {template void checked_delete (t * x) {// ... Other stuff ... delete x;}} Class Test {~ Test () {} friend void boost :: checked_delete (test * x); // the original code};

INT main () {boost :: checked_delete (new test);}

Compile the above code with your compiler and then compare with our results. If you have seen TV programs "Family Feud",

You can emerge in Richard Dawson: "Survey Saaaaays". (See Table 1) Table 1: The results of compiling Example 1 on various compilersCompiler Result Error MessageBorland 5.5 OK Comeau 4.3.0.1 OK EDG 3.0.1 OK Intel 6.0.1 OK gcc 2.95.3 Error `boost :: checked_delete (Test * ) 'SHOULD HAVE BEEN DECLARED INSIDE

`boost'gcc 3.1.1 error` void boost :: checked_delete (test *) 'SHOULD HAVE BEEN DECLARED INSIDE

`Boost'gcc 3.2 Error` void boost :: checked_delete (Test *) 'should have been declared inside `boost'Metrowerks 8.2 Error friend void boost :: checked_delete (Test * x); name has not beendeclared in namespace / classMS VC 6.0 Error Nonexistent Function Oost :: Checked_Delete 'Specified As Friendms VC 7.0 OK MS VC 7.1 Beta Error Oost :: Checked_Delete': Not a Function

For this use case, the test results show that this grammar is not well accepted by the current compiler. By the way, Comeau, EDG,

Intel's compiler passes, this is not surprising, because they all based on EDG C implementation; five different C languages ​​tested

In the implementation, three versions are not accepted (GCC, Metrowerks, Microsoft), two can be (Borland, EDG).

We tried another standard compatible method, test rules # 1: // example 2: the Other way to declare friendsship // namespace boost {template void checked_delete (t * x) {// ... taher Stuff ... delete x;}} Class test {~ test () {} friend void boost :: checked_delete <> (test * x);

INT main () {boost :: checked_delete (new test);}

Or, equivalent, we can also write: Friend Void Boost :: Checked_Delete (Test * x); where to try when trying to try with our compiler (We twistur compilers' tails, the results show It is supported

too much. (See Table 2) Table 2: The results of compiling Example 2 on various compilersCompiler Result Error MessageBorland 5.5 OK Comeau 4.3.0.1 OK EDG 3.0.1 OK Intel 6.0.1 OK gcc 2.95.3 Error `boost :: checked_delete (Test * ) 'SHOULD HAVE BEEN DECLARED INSIDE

`boost'gcc 3.1.1 error` void boost :: checked_delete (test *) 'SHOULD HAVE BEEN DECLARED INSIDE

`Boost'gcc 3.2 Error` void boost :: checked_delete (Test *) 'should have been declared inside `boost'Metrowerks 8.2 OK MS VC 6.0 Error nonexistent function deterrence oost :: checked_delete' specified as friendMS VC 7.0 OK MS VC 7.1 beta OK Rules # 1 Have a lot of grasping - Example 2 Working in all current version compilers except GCC and all of Microsoft Visual C 6.0

Old version of the compiler.

Between: Yes "Namespace" makes them confused -------------------------------------------------------------------------------------- ---------------------------------------- Note: If we are trying to make it a friend Yuan's function template is not in different namespaces, then we can be in a few today.

All of these compilers correctly make rules # 1: // example 3: if Only checked_delete // us't in a namespace ... //// no longer in boost :: template void checked_delete (T * x) {// ... Other stuff ... delete x;}

Class test {// no longer nesed "Boost:" Friend void check_delete (test * x);

INT main () {checked_delete (new test);}

Test results show ... (see Table 3). Therefore, most of the problems on the compiler that cannot be handled Example 1 is: Explicitly declaring other namespaces

A special version of the function template (So The Problem on Most Compilers That CAN't Handle Example

1 IS Specification Declaring Friendship for a Function Template Specialization in Another

Namespace. (吆, I will miss this sentence three times.) Hey, the compiler of the poster - Microsoft Visual C 6.0, cannot

Handling such a simple little problem. Table 3: The results of compiling Example 3 on various compilersCompiler Result Error MessageBorland 5.5 OK Comeau 4.3.0.1 OK EDG 3.0.1 OK Intel 6.0.1 OK gcc 2.95.3 OK gcc 3.1.1 OK gcc 3.2 OK Metrowerks 8.2 OK MS VC 6.0 ERROR SYNTAX Error (Just Can't Handle IT) MS VC 7.0 Error Friend Declaration IncorRectly Interpreted As Declaring A Brand-New

(and undefined) ORDINARY NON-TEMPLATE FUNTAXMS VC 7.1 Beta OK two can't work -------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------- ------ When this problem occurs on UseNet, some respondents are recommended to use use-declaration (or, equivalent USING-DIRECTIVE)

, Then use the friend of the unworthy domain definition:

Namespace Boost {Template Void Checked_Delete (T * x) {// ... Other Stuff ... delete x;}}

Using boost :: checked_delete;

Class test {~ test () {}

// NOT The Template Specialization! Friend Void Checked_Delete (Test * x);

The above-mentioned friend declaration will fall into rule # 4: "Finally, that name must be limited to [class or Namespace name], and declared (or

Repeatedly declared) a normal (non-template) function. "This is actually a new ordinary non-template in the closed namespace.

Function, called: Checked_Delete (Test *).

If you try the above code, many compilers will refuse, prompts that checked_delete () is not defined; all compilers will refuse, such as

If you try to use a friend relationship and place a private member in the Boost :: Checked_Delete () template.

Finally, an expert recommends a slight modification of it - using "Using" and uses the syntax of the template "<>":

Namespace Boost {Template Void Checked_Delete (T * x) {// ... Other Stuff ... delete x;}}

Using boost :: checked_delete;

Class test {~ test () {} friend void checked_delete <> (test * x); // legal?};

The above code may not be a legitimate C code - the standard does not clearly explain it, and there is an open topic requirements in the standard committee.

This should be legitimate, tend to think it should not legal; in the real world, all the current version of the current version I have tried

Reject it. Why do people think it should be legitimate? Based on consistency, because using exists

Easy to use Names - use the type name when calling functions and decimation in variables and denominations. Declaration is different: as you must be in the template

The namespace declared its specialty version (you can't "pass a using" to complete it in another namespace), so you should

Only a special version is a friend (cannot "through a using" by the limited space of the template.

You Must Declare A Template Specialization In The Template's Original Namespace (you can't

Do It in Another Namespace "THROUGH A Using")

Using ")).

to sum up------------------------------------------------- -------------------------------

In order to declare the special version of a function template as a friend, you can choose one in two syntax: // from example 1 Friend Void Boost :: Checked_Delete (Test * x);

// from example 2: add <> or friend void boost :: checked_delete <> (test * x);

This article has proven that if "<>" or "Test" is not written in Example 2, it will be highly portable cost.

Guidelines: Explicitly indicating your intentions. When you have a special version of the function template as a friend, always explicitly add at least one pair.

<> "Template symbol. For example:

Namespace Boost {Template Void Checked_delete (t * x);} Class test {friend void boost :: checked_delete (test * x); // Bad friend void boost :: checked_delete <> (TEST * X); / / Good};

If your compiler does not support any one of these two friend declarations, then you have to set the function you have to be public - but add one

Comment explains why do you do this, and remind you to change it to your private place as long as you upgrade your compiler. [Note 1]

thank------------------------------------------------- ------------------------------- Thanks to John Potter for Comments on Drafts of this Material.

Note------------------------------------------------- ------------------------------- [1] There area Other Workarounds, But They're All Much More Cumbersome. For Example You

Could Create a Proxy Class Inside Namespace Boost And Befriend That.

转载请注明原文地址:https://www.9cbs.com/read-26709.html

New Post(0)