Object Swapping, Part 7: How Do You Call It?
We've talked about why swapping is important, and about how to use it to implement other operations such as assignment. We shall now look more closely at how to use it.
More Insights
White Papers
- A How-To Guide on Using Cloud Services for Security-Rich Data Backup
- How to test and launch a world-class application
Reports
- Best Practices: Using Apple's Global Proxy to Boost Mobile Security
- InformationWeek 2013 IT Spending Priorities Survey
Webcasts
- The Untapped Potential of Mobile Apps for Commercial Customers
- Secure Cloud: Taking Advantage of the Intelligent WAN
Suppose we define a class X, and we write our own swap operation for it:
class X { /* code */ };
void swap(X& x, X& y) { /* more code */ }
If we have two variables of class X, and we want to swap them, we can call our swap function:
void foo() {
X x1, x2;
// …
swap(x1, x2);
// and so on
}
If instead of calling swap(x1, x2), we mistakenly call std::swap(x1, x2), our program will seem to work — so long as our class X supports copying (or moving) and assignment. The reason is that std::swap looks something like this:
template<class T> void swap(T& t1, T& t2) {
T temp = t1;
t1 = t2;
t2 = temp;
}
When we call std::swap(x1, x2), we are saying that we want to use swap from the standard library rather than the one that we defined along with our class. Fortunately or unfortunately, depending on your point of view, the standard-library version is capable of swapping most classes — but we can expect that it will probably do so more slowly than a class-specific swap operation.
So it's not a good idea to call std::swap explicitly, because doing so rules out the possibility of using a class-specific version. On the other hand, consider this example:
void bar() {
int i = 0, j = 0;
// …
swap(i, j); // Danger!
// and so on
}
We have changed the names of our function and its variables, and changed the variables' type from X to int. Now, we have no assurance that this program will compile. The reason is that in order to swap integers, we must use std::swap; and we do not know whether std::swap was ever imported into the global namespace. If we want to be sure that this code will work, we had better write it this way:
void bar() {
int i = 0, j = 0;
// …
std::swap(i, j); // Much better.
// and so on
}
This is an unfortunate situation, because it implies that in order to swap two variables, we have to know their types. Requiring such knowledge makes generic programming much more difficult.
Fortunately, there is a way out of this dilemma:
void bar() {
using std::swap;
int i = 0, j = 0;
// …
swap(i, j); // Best of all.
// and so on
}
On the surface, this code looks like it should not work. After all, it imports std::swap into the scope of bar, so when we call swap(i, j), one would think that it would find only std::swap. However, the code does work — because of a C++ feature called argument-dependent lookup (abbreviated as ADL). ADL says that when you call a function without using :: as part of its name, then in addition to the normal scope lookup, the compiler looks for the function name in every namespace that contains the definition of the call's argument types.
In other words, when we call swap(x, y), and x and y are objects of class X, the compiler finds the namespace that contains the definition of class X — here, the global namespace — and looks there for the definition of swap in addition to looking in the normal places.
In fact, ADL allows this technique for calling swap to work even if we define our class in a namespace of its own:
namespace Mine {
class X { /* code */ };
void swap(X& x, X& y) { /* more code */ }
};
void baz() {
using std::swap;
int i = 1, j = 2;
Mine::X x1, x2;
// …
swap(i, j);
swap(x1, x2);
// and so on
}
In this example, the call to swap(i, j) will find std::swap, because that is the only definition of swap directly available within the scope of baz. Moreover, the call to swap(i, j) will find Mine::X::swap because of ADL. Even though swap is not defined in the global namespace at all in this example, overloading will find the correct version for each call.
,p>When you swap variables, and you don't want to have to know their types when you're writing your code, this technique is worth remembering.

