Objective C ++ Reading Notes (15)

Source: Internet
Author: User

Clause 25: Write A swap function without throwing an exception.

Consider support for a non-throwing swap

Swap is an interesting function. It was first introduced as part of STL, and later became the pillar of exception-safeprogramming and a common mechanism for dealing with the possibility of self-assignment. Because swap is so useful, it is very important to implement it correctly, but with its extraordinary importance it comes a series of extraordinary complexities.

The value of two swap objects is to assign their own values to each other. By default, swap actions can be completed by the swap algorithm provided by the standard library. The typical implementation is exactly as expected:

Namespace std {

Template <typename T> // std: Typical Implementation of swap, replacing the values of a and B
Void swap (T & a, T & B)
{
T temp ();
A = B;
B = temp;
}
}

As long as your type supports copying (through the copy constructor and the copy assignment operator), the default swap implementation can exchange objects of the T type without any special support work. It involves copying three objects: From a to temp, from B to a, and from temp to B. For some types, these assignment actions are unnecessary.

The most important of such a type is a pointer that points to a type that contains real data. A common manifestation of this design method is the "pimpl method" ("pointerto implementation "). If you design the Widget class in this way, it may be like this:

Class WidgetImpl {// class designed for Widget data
Public:
...

Private:
Int a, B, c; // there may be a lot of data, meaning that the replication takes a long time
Std: vector <double> v;
...
};

Class Widget {// This class uses the pimpl Method
Public:
Widget (const Widget & rhs );
Widget & operator = (const Widget & rhs)
{// Copy the WidgetImpl object when copying a Widget
...
* PImpl = * (rhs. pImpl );
...
}
...

Private:
WidgetImpl * pImpl; // pointer, indicating that the object contains Widget data
};

In order to exchange the values of these two Widget objects, what we actually need to do is to exchange their pImpl pointers. However, the default switching algorithm not only needs to copy three Widgets, but also has three WidgetImpl objects, the efficiency is too low. When we exchange Widgets, we should tell std: swap that the method we intend to perform the exchange is to exchange their internal pImpl pointers. The formal statement for this method is: to specifically define std: swap for widgets.

Class Widget {
Public:
...
Void swap (Widget & other)
{
Using std: swap; // This statement is necessary
Swap (pImpl, other. pImpl); // Replace the pImpl pointer of a Widget if you want to replace it
}
...
};

Namespace std {

Template <> // This is a special version of std: swap for "T is a Widget ".
Void swap <Widget> (Widget & a, Widget & B)
{
A. swap (B); // to replace a Widget, call its swap member function.
}
}

The "template <>" at the beginning of this function indicates that it is a fully-specialized version of std: swap, the "<Widget>" after the function name indicates that this specific version is designed for "T is a Widget. In other words, this version is enabled when a common swap template is used for Widgets. Generally, changing the content in std namespace is not allowed, but it is allowed to create a special version for a standard template (such as swap) so that it is proprietary to our own class (such as widgets ).

We declare a public member function named swap in the Widget to do the real replacement work, and then specialize in std: swap to call that member function. In this way, not only can compilation be performed, but also be consistent with STL containers. All STL containers provide both public swap member functions and std: swap special functions to call these member functions.

 

However, if widgets and WidgetImpl are class templates rather than classes, we may try to parameterize the data types in WidgetImpl:

Template <typename T>
Class WidgetImpl {...};

Template <typename T>
Class Widget {...};

Solution 1 is as follows:

Namespace std {
Template <typenameT>
Void swap <Widget <T> (Widget <T> & a, Widget <T> & B)
{A. swap (B );}
} // Error, invalid!

Although C ++ allows partialspecialization of class templates, function templates are not allowed to do so.

Solution 2 is as follows:

Namespace std {

Template <typename T> // std: an overloaded version of swap
Void swap (Widget <T> & a, Widget <T> & B)
{A. swap (B );}
} // This is also invalid

Generally, there is no problem with the overload function template, but std is a special namespace and its rules are quite special. It recognizes templates in fully specialized std, but it does not recognize the addition of new templates (or classes, functions, and anything else) in std ).

The correct method enables others to call swap, and enables us to get a more efficient template-specific version. We still declare a non-member swap to call the member swap, but do not declare the non-member function as the std: swap special or heavy load. For example, if the Widget-related functions are in namespace WidgetStuff:

Namespace WidgetStuff {
... // Templated WidgetImpl, etc.

Template <typename T> // contains the swap member function
Class Widget {...};
...

Template <typename T> // non-member swap function, which does not belong to the std namespace
Voidswap (Widget <T> & a, Widget <T> & B)
{A. swap (B );}
}

Now, if a code somewhere intends to replace two Widget objects and swap is called, the C ++ name search rule will find the private version of the Widget in WidgetStuff.

 

From the customer's point of view, suppose you have written a function template to exchange the values of two objects. Which swap should be called? Is the general version in std, the general version of std, or the dedicated version of T (definitely not in std )? If the dedicated version T exists, call it; otherwise, you can call the universal version in std. In this way, you can meet your hopes:

Template <typename T>
Void doSomething (T & obj1, T & obj2)
{
Using std: swap; // make std: swap available in this function
...
Swap (obj1, obj2); // the best version of swap called for T-type objects
...
}

When the compiler sees this swap call, it will look for the correct swap version for the call. If T is a Widget in namespaceWidgetStuff, the compiler will find the swap in WidgetStuff using the parameter dependency lookup (argument-dependent lookup). If the T-dedicated swap does not exist, the compiler will use the swap in std, this is because the using declarative form in this function makes std: swap visible here. Nevertheless, compared with general templates, compilers prefer T-specific std: swap features. Therefore, if std: swap features T features, the specific version will be used.

Be careful not to limit the call, because this will affect C ++'s selection of appropriate functions:

Std: swap (obj1, obj2); // the wrong way to callswap

This will force the compiler to only consider the swap in std (including any template-specific), and thus eliminate the possibility of defining a more suitable T-dedicated version elsewhere to be called.

 

Summary:

First, if the default Implementation of swap provides acceptable performance for your class or class template, you do not need to do anything. Any operations on objects trying to exchange types will be supported by the default version and work well.

Second, if swap's default implementation efficiency is insufficient (this almost always means that your class or template uses some pimpl method), follow these steps:

1. A public swap member function is provided to efficiently exchange the values of two objects of your type. This function should never throw an exception.

2. Provide a non-member swap in the same namespace of your class or template, and use it to call your swap member function.

3. If you have written a class (not a class template), you can define std: swap for your class and make it call your swap member function.

Finally, if you call swap, make sure that your function contains a using declaration to make std: swap visible, and then do not use any namespace modifier when you call swap.

Warning do not let the swap member version throw an exception. This is because one of swap's most important applications is to provide powerful exception-safety (exception-safety) guarantees for classes (and class templates. If you have written a custom version of swap, you can provide a more efficient method for exchanging values in typical cases, and ensure that this method does not throw an exception. These two swap features are closely combined, because efficient switching is almost always based on built-in types (such as pimpl pointers, built-in operations will never throw an exception.

· If std: swap is inefficient for your type, provide a swap member function and ensure that your swap will not throw an exception.

· If www.2cto.com provides a member swap, provide a non-member swap that calls the member swap. For classes (non-templates), the std: swap should also be made special.

· When swap is called, use a using declarative form for std: swap, and do not use any namespace modifiers when swap is called.

· It is good to define a fully-specific std template for a user, but never try to add anything new to std.



From pandawuwyj's column

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.