Overloading global swap for user-defined type
The C++ standard prohibits declaring types or defining anything in namespace std
, but it does allow you to specialize standard STL templates for user-defined types.
Usually, when I want to specialize std::swap
for my own custom templated type, I just do:
namespace std
{
template <class T>
void swap(MyType<T>& t1, MyType<T>& t2)
{
t1.swap(t2);
}
}
...and that works out fine. But I'm not entirely sure if my usual practice is standard compliant开发者_StackOverflow中文版. Am I doing this correctly?
What you have is not a specialization, it is overloading and exactly what the standard prohibits. (However, it will almost always currently work in practice, and may be acceptable to you.)
Here is how you provide your own swap for your class template:
template<class T>
struct Ex {
friend void swap(Ex& a, Ex& b) {
using std::swap;
swap(a.n, b.n);
}
T n;
}
And here is how you call swap, which you'll notice is used in Ex's swap too:
void f() {
using std::swap; // std::swap is the default or fallback
Ex<int> a, b;
swap(a, b); // invokes ADL
}
Related: Function template specialization importance and necessity
Why won't you just define swap in MyType's namespace and exploit argument-dependent lookup power?
Because of argument dependent (aka Koenig) lookup, I believe you can specify your own swap in the namespace of the type you want it for and it will be found in preference to ::std::swap
. Also, I believe the template for ::std::swap
will expand differently for classes that have their own swap member function and so you can add that member function to the class and that will be used for your type.
Edit
See Scott Meyer's article: See Effective C++ 3rd Edition, item 25: Consider support for a non-throwing swap (p106-p112) for a confirmation of my answer.
Original answer
Scott Meyers wrote about this, so my answer comes from memory.
First, define a swap function in the namespace of your class. For example :
namespace MyNamespace
{
class MyClass { /* etc. */ } ;
template<typename T>
class MyTemplate { /* etc. */ } ;
void swap(MyClass & lhs, MyClass & rhs)
{
// the swapping code (**)
}
template<typename T>
void swap(MyTemplate<T> & lhs, MyTemplate<T> & rhs)
{
// the swapping code (**)
}
}
Then, if possible (it is not always possible for templated classes (*) ), specialize the swap function in the namespace std. For example :
namespace std
{
template<>
void swap<MyNamespace::MyClass>(MyNamespace::MyClass & lhs, MyNamespace::MyClass & rhs)
{
// the swapping code (**)
}
// The similar code for MyTemplate is forbidden, so don't try
// to uncomment it
//
// template<typename T>
// void swap<MyNamespace::MyTemplate<T> >(MyNamespace::MyTemplate<T> & lhs, MyNamespace::MyTemplate<T> & rhs)
// {
// // the swapping code (**)
// }
}
The, when using the swap function, do it indirectly, importing the std swap function into your scope. For example :
void doSomething(MyClass & lhs, MyClass & rhs)
{
// etc.
// I swap the two objects below:
{
using std::swap ;
swap(lhs, rhs) ;
}
// etc.
}
void doSomethingElse(MyTemplate<int> & lhs, MyTemplate<int> & rhs)
{
// etc.
// I swap the two objects below:
{
using std::swap ;
swap(lhs, rhs) ;
}
// etc.
}
As soon as I have access to my books, I'll post here the exact reference.
- (*) template partial specialization of a function is forbidden
- (**) of course, a good pattern is to have a "swap" method declared in the class, have the swap functions call the swap method, and have the user call the swap function.
What you're doing is an overload and not a template specialization. The standard does not allow you to overload inside namespace std
(17.6.4.2.1 §1)
The behavior of a C++ program is undefined if it adds declarations or definitions to
namespace std
or to a namespace withinnamespace std
unless otherwise specified. A program may add a template specialization for any standard library template tonamespace std
only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.
Therefore, prefer to put your template type into your own namespace and define a non-member swap()
within that namespace (this is not strictly necessary, but good practice). This way swap(x,y)
will work from anywhere via argument dependent lookup (ADL, aka Koenig lookup), if x
or y
are in your namespace.
namespace my_ns {
template <typename T> class MyType
{
public:
void swap( MyType & other ) noexcept;
};
template <typename T>
void swap( MyType<T> & lhs, MyType<T> & rhs ) noexcept
{
lhs.swap(rhs);
}
} // namespace my_ns
Code using swap()
should normally use the using namespace std
technique. This way your version of swap will be found by ADL and it will be prefered to the std::swap()
function, since it is more specialized.
// client code
MyType<Bla> x, y;
/* ... some code ... */
using namespace std;
swap( x, y ); // will call your swap version
Define your type and your swap function in the same namespace:
namespace foo
{
struct Bar
{
};
void swap(Bar & t1, Bar& t2)
{
// whatever
}
}
int main()
{
using std::swap;
foo::Bar a, b;
swap(a, b); // Argument-dependent lookup chooses foo::swap
// if it exists, or else reverts to std::swap
}
Define own swap
. This function must call std::swap for any type T except your types.
namespace help // my namespace
{
template <class T>
void swap(T& t1, T& t2)
{
::std::swap(t1, t2); // Redirect to std for almost all cases
}
// My special case: overloading
template <class T>
void swap(MyType<T>& t1, MyType<T>& t2)
{
t1.swap(t2);
}
} // namespace help
// Sample
int main()
{
MyType<int> t1, t2; // may be add initialization
int i1=5, i2=7;
help::swap(t1, t2); // Your swap
help::swap(i1, i2); // Redirect to std::swap
}
精彩评论