Topic: Specializing std::swap?
Author: Alan Griffiths <alan@octopull.demon.co.uk>
Date: 1999/12/20 Raw View
In article <3857F3D1.4E926EEA@wizard.net>, James Kuyper
<kuyper@wizard.net> writes
>
>alan_griffiths@my-deja.com wrote:
>...
>> I don't like to contemplate changing the name lookup rules, but allowing
>> overloading on UDTs within namespace std looks relatively safe and would
>> make it much easier to provide usable library code.
>
>Standard library functions may have hidden interactions with each other.
>std::malloc() and std::free() are good examples.
Ugh, I'd forgotten all those "impure" functions. I'd come at it from
two scenarios:
o implementing a container to be as much like a standard container as
possible (where I _really_ want to partially specialise std::swap -
see the discussion below).
o trying to implement a numeric type that emulates the fundamental
types as much as possible (where I'd like to overload the <cmath>
functions).
>If users were allowed
>to overload functions in namespace std, the standard would have to
>specify what each function does seperately, well enough to allow a
>user-defined versions to work together with implementation-defined
>versions. It would also require exposing a standard interface to details
>that are currently allowed to vary from one implementation to another.
>
>This wouldn't be impossible, but it would be a lot more work, and
>standardizing the currently hidden details behind the standard library
>would reduce the ease, efficiency, and even the possibility of
>implementating C on various unusual platforms.
Let me give my "motivating example" (something very like it happened)
and see if we can find some relaxation of the rules that addresses both
of our concerns...
Cast: two "public" programmers Janet and John, and a library
implementor (me).
Janet writes a class using std::vector<>, and provides a swap()
method and a free swap() function. (She'd been paying attention
during my seminar on exception safety.) The swap function looked
something like:
C& C::swap(C& that)
{
using std::swap;
swap(a, that.a);
swap(b, that.b);
swap(c, that.c);
swap(d, that.d);
. . .
}
Some months later John comes along and decides that a container
(call it arg::vector) that I wrote would be better than std::vector.
He changes the class definition, re-runs the unit test harness and
commits the change.
The problem is, of course, that the resulting code /1/ no longer
meets the strong exception safety guarantee and /2/ is a lot slower.
(I had provided an efficient, non-throwing swap<> in namespace arg.)
Now I feel that I failed to provide a container that was sufficiently
like a standard container. At the time I "rectified" this by partially
specialising std::swap (which I now realise was illegal).
As the rules stand the problem is John's lack of knowledge. [I don't
think is correct to crime him for this - it would be a very rare code
inspection that picked up on this]. There are A LOT of John's out there
and I try to cater for them (and for myself on a bad day).
I think a very similar situation could arise if I were to implement an
arg::big_int class and to provide abs() and its kin in namespace arg.
(In the alternate universe where it is allowed I can put overloads in
namespace std and the user _cannot_ then name the incorrect version.)
--
Alan Griffiths (alan@octopull.demon.co.uk) http://www.octopull.demon.co.uk/
ACCU Chairman (chair@accu.org) http://www.accu.org/
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Darin Adler <darin@bentspoon.com>
Date: 1999/12/13 Raw View
James Kuyper <kuyper@wizard.net> wrote:
> Perhaps what you're thinking of is the fact that, unlike class
> templates, function templates cannot be partially specialized; they can
> only be fully specialized. However, that's not relevant for your example
> - it is fully specialized.
You and others have pointed out that my example is legal, because it is
explicit specialization of a function template, which is allowed, although
partial specialization of a function template is not. But what if my example
class had been a class template instead?
// header file
#include <algorithm>
namespace N {
template<typename T>
class U
{
public:
// ...
void swap(U&);
U& operator=(const U&);
// ...
};
}
namespace std {
template<typename T>
inline void swap(N::U<T>& one, N::U<T>& other)
{
one.swap(other);
}
}
// implementation file
namespace N {
template<typename T>
void U<T>::swap(U& other)
{
std::swap(m1, other.m1);
std::swap(m2, other.m2);
}
template<typename T>
U<T>& U<T>::operator=(const U& other)
{
U copy(other);
swap(copy);
return *this;
}
}
Is this still legal? Does the the std::swap definition above still qualify
as an explicit specialization? Or is it overloading (not allowed in
namespace std)? Or something else?
-- Darin
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: James Kuyper <kuyper@wizard.net>
Date: 1999/12/14 Raw View
Darin Adler wrote:
>
> James Kuyper <kuyper@wizard.net> wrote:
>
> > Perhaps what you're thinking of is the fact that, unlike class
> > templates, function templates cannot be partially specialized; they can
> > only be fully specialized. However, that's not relevant for your example
> > - it is fully specialized.
>
> You and others have pointed out that my example is legal, because it is
> explicit specialization of a function template, which is allowed, although
> partial specialization of a function template is not. But what if my example
> class had been a class template instead?
If your example had been a class template, you'd be allowed to partially
specialize it, as well as fully specializing. Since the example below
involves a function template, that's not an option.
> // header file
>
> #include <algorithm>
>
> namespace N {
>
> template<typename T>
> class U
> {
> public:
> // ...
> void swap(U&);
> U& operator=(const U&);
> // ...
> };
>
> }
>
> namespace std {
>
> template<typename T>
> inline void swap(N::U<T>& one, N::U<T>& other)
> {
> one.swap(other);
> }
>
> }
>
...
> Is this still legal? Does the the std::swap definition above still qualify
> as an explicit specialization? Or is it overloading (not allowed in
> namespace std)? Or something else?
No, it's an overloaded function template, and as such something that
user code is not allowed to add to namespace std.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/12/14 Raw View
Biju Thomas wrote:
>
> Darin Adler wrote:
> >
> > Recently, someone pointed out to me that the swap definition in the above
> > code is illegal. I thought that I was specializing a swap function template,
> > but in fact this is not possible. As I understand it, there's no such thing
> > as a specialization of a function template. It's possible to overload a
> > function template, but not specialize it. After hearing this, I am not even
> > sure if the syntax is correct in the code above.
>
> You might have been thinking about _partial_ specialization of fucntion
> templates, which is not allowed. But, explicit specialization is
> allowed. Your code will work fine.
However, this just means that the problem is restricted to templates:
template<class T> class MyContainer { ... };
// Obvious specialisation, not allowed:
template<class T>
std::swap<MyContainer<T> >(MyContainer<T>& a,
MyContainer<T>& b)
{
a.swap(b);
}
If full specialisation is allowed, partial specialisation should be,
too.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: alan_griffiths@my-deja.com
Date: 1999/12/15 Raw View
In article <38568FA2.EB0F0323@physik.tu-muenchen.de>,
Christopher Eltschka <celtschk@physik.tu-muenchen.de> wrote:
[snip]
> However, this just means that the problem is restricted to templates:
Actually I think there is a wider problem - that includes overloading on
UDTs.
Suppose for a moment I want to reproduce a std function (take
swap<my_namespace::MyTemplate<T> > and abs(my_namespace::MyType) as
examples) as support for types I am developing.
If I place this in my namespace then it NEED NOT be found by user code.
(Because the lookup rules may encounter a class member of the same name
in client code.)
In particular the client code may be templated and developed in
ignorance of the types I'm developing. Suppose the client code expects
to find a container then code like:
Client& Client::swap(Client& that)
{
using std;
swap(member, that.member);
. . .
return *this;
}
Is reasonable, except that if the container changes from one in std to
MyTemplate it is broken unless either:
/1/ std::swap has been partially specialised
/2/ the name lookup rules find my_namespace::swap
It is similarly plausible that client code like:
int Client::abs()
{
return std::abs(member);
}
May become silently broken when the type of member becomes MyType.
I don't like to contemplate changing the name lookup rules, but allowing
overloading on UDTs within namespace std looks relatively safe and would
make it much easier to provide usable library code.
--
Alan Griffiths (alan.griffiths@experian.com, +44 115 934 4517)
Senior Systems Consultant, Experian
Sent via Deja.com http://www.deja.com/
Before you buy.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: James Kuyper <kuyper@wizard.net>
Date: 1999/12/15 Raw View
alan_griffiths@my-deja.com wrote:
...
> I don't like to contemplate changing the name lookup rules, but allowing
> overloading on UDTs within namespace std looks relatively safe and would
> make it much easier to provide usable library code.
Standard library functions may have hidden interactions with each other.
std::malloc() and std::free() are good examples. If users were allowed
to overload functions in namespace std, the standard would have to
specify what each function does seperately, well enough to allow a
user-defined versions to work together with implementation-defined
versions. It would also require exposing a standard interface to details
that are currently allowed to vary from one implementation to another.
This wouldn't be impossible, but it would be a lot more work, and
standardizing the currently hidden details behind the standard library
would reduce the ease, efficiency, and even the possibility of
implementating C on various unusual platforms.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Darin Adler <darin@bentspoon.com>
Date: 1999/12/11 Raw View
Because I've read Herb Sutter's excellent description of the swap technique
for implementing an exception-safe operator= (in Guru of the Week and his
new book Exceptional C++), most of my classes have a member function named
swap. Up until now, I've been implementing it like this:
// header file
#include <algorithm>
namespace N {
class U
{
public:
// ...
void swap(U&);
U& operator=(const U&);
// ...
};
}
namespace std {
template<> inline void swap(N::U& one, N::U& other)
{
one.swap(other);
}
}
// implementation file
namespace N {
void U::swap(U& other)
{
std::swap(m1, other.m1);
std::swap(m2, other.m2);
}
U& U::operator=(const U& other)
{
U copy(other);
swap(copy);
return *this;
}
}
The idea here is to make the efficient non-throwing version of swap defined
in the member function available to the standard library implementation by
specializing the std::swap function template. The more-efficient swap will
be used any time a generic algorithm swaps two objects of this type. In
fact, I suggested a change to iter_swap to ensure that this optimization
will be performed as often as possible. Andrew Koenig submitted a library
defect report about that which is currently active:
<http://anubis.dkuug.dk/JTC1/SC22/WG21/docs/lwg-active.html#187>.
Recently, someone pointed out to me that the swap definition in the above
code is illegal. I thought that I was specializing a swap function template,
but in fact this is not possible. As I understand it, there's no such thing
as a specialization of a function template. It's possible to overload a
function template, but not specialize it. After hearing this, I am not even
sure if the syntax is correct in the code above.
Worse, the standard [17.4.3.1/1] allows me to specialize a template in a
standard header, but does not allow me to overload a function with a new
definition in namespace std, even if it's a function template. So the swap
operation must move out of namespace std. Herb's chapter "Name Lookup,
Namespaces, and the Interface Principle" in Effective C++ pushes me further
in this direction, suggesting that a function like swap belongs in namespace
N alongside the class U to get the best results with Koenig lookup.
This creates a few new problems. Code that explicitly calls std::swap won't
get the more efficient version defined for class U -- as I understand it,
Koenig lookup doesn't apply when you specify the namespace explicitly. When
I tested with my compiler, I didn't even get the appropriate swap when I put
a "using std::swap" in; the using directive seemed to interfere with Koenig
lookup.
I hate the idea that if the overload of swap is not found, the code will
work but be inefficient and possibly throw an exception where one is not
expected. U::swap above may have this problem because it calls std::swap on
m1 and m2, and either of these may have a more efficient non-throwing
version of swap. Since I want Koenig lookup to work and so can't explicitly
qualify std::swap, I have to make sure that the member function named swap
doesn't hide the global function named swap. So it may be best to omit the
member function completely.
Here's the "corrected" version of class U.
// header file
namespace N {
class U
{
public:
// ...
friend void swap(U&, U&);
U& operator=(const U&);
// ...
};
}
// implementation file
#include <algorithm>
namespace N {
void swap(U& one, U& other)
{
using namespace std;
swap(one.m1, other.m1);
swap(one.m2, other.m2);
}
U& U::operator=(const U& other)
{
U copy(other);
swap(*this, copy);
return *this;
}
}
As I understand it, the same issues apply to specializations of std::less
and some other function templates in the library.
At this point I have a few questions:
1) Is the version of std::swap that I define in my original example
above illegal?
2) Is there a better way to write swap for U (preferably without "using
namespace std;")? The desire is that it will use an overloaded
implementation of swap found by Koenig lookup in another namespace for each
member, but fall back on std::swap if such a function does not exist.
3) Should I deprecate use of "std::swap" and "using std::swap" in all my
programs (at least in any generic parts)? It seems this may be necessary
since either of these forms will result in a call to the less efficient
standard library function template implementation of swap even when a more
efficient overload is defined as "part of" the class. (Note that I am using
"part of" in the sense described in Exceptional C++.)
4) Is there a guarantee that algorithms in the standard library will
call swap without an explicit "std::" qualifier? The library implementation
can probably omit the "std::" since the implementation is normally inside
namespace std. If it does not use "std::", it seems it will pick up the
non-throwing swap implementation from the namespace that the class is
defined in, due to Koenig lookup. If the library is allowed to explicitly
call "std::swap" instead, it would really bum me out. I might even go so far
as to say it constitutes a defect in the standard.
5) What else did I get wrong in the discussion above?
-- Darin
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: wmm@fastdial.net
Date: 1999/12/12 Raw View
In article <B476D10C.D0B2%darin@bentspoon.com>,
Darin Adler <darin@bentspoon.com> wrote:
>
> As I understand it, there's no such thing
> as a specialization of a function template. It's possible to overload a
> function template, but not specialize it. After hearing this, I am not even
> sure if the syntax is correct in the code above.
Could you elaborate on what you mean by this? It is certainly
possible to provide an explicit specialization of a function
template -- 14.7.3p1 says so and even provides an example of it
(sort<char*>).
> Worse, the standard [17.4.3.1/1] allows me to specialize a template in a
> standard header, but does not allow me to overload a function with a new
> definition in namespace std, even if it's a function template.
You cannot provide a (non-template function) overload for a
standard function template, but you can provide an explicit
specialization for one (subject to the requirement that it
depend on a user-defined name with external linkage).
What am I missing?
--
William M. Miller, wmm@fastdial.net
OnDisplay, Inc. (www.ondisplay.com)
Sent via Deja.com http://www.deja.com/
Before you buy.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Biju Thomas <b_thomas@ibm.net>
Date: 1999/12/12 Raw View
Darin Adler wrote:
>
> Recently, someone pointed out to me that the swap definition in the above
> code is illegal. I thought that I was specializing a swap function template,
> but in fact this is not possible. As I understand it, there's no such thing
> as a specialization of a function template. It's possible to overload a
> function template, but not specialize it. After hearing this, I am not even
> sure if the syntax is correct in the code above.
You might have been thinking about _partial_ specialization of fucntion
templates, which is not allowed. But, explicit specialization is
allowed. Your code will work fine.
> Herb's chapter "Name Lookup,
> Namespaces, and the Interface Principle" in Effective C++
I knew someone was going to call it 'Effective C++' one day. :-)
> pushes me further
> in this direction, suggesting that a function like swap belongs in namespace
> N alongside the class U to get the best results with Koenig lookup.
>
This guideline doesn't apply to specializations of standard library
functions. Otherwise, as you note, Koenig lookup won't be applied.
Herb's rule might be about new standalone functions that you add, not to
specializations of template functions in standard library.
> 4) Is there a guarantee that algorithms in the standard library will
> call swap without an explicit "std::" qualifier?
There is a footnote under section 17.4.3.1 (163) saying that library
implementations must be prepared to work with specializations of the
templates in the standard library. So, I think you don't have to worry
about this.
--
Regards,
Biju Thomas
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: James Kuyper <kuyper@wizard.net>
Date: 1999/12/12 Raw View
Darin Adler wrote:
....
> but in fact this is not possible. As I understand it, there's no such thing
> as a specialization of a function template. It's possible to overload a
> function template, but not specialize it. After hearing this, I am not even
> sure if the syntax is correct in the code above.
Section 14.8 is devoted to describing function template specializations.
It would be much shorter if it only said "not allowed" :-)
Perhaps what you're thinking of is the fact that, unlike class
templates, function templates cannot be partially specialized; they can
only be fully specialized. However, that's not relevant for your example
- it is fully specialized.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: James Kuyper <kuyper@wizard.net>
Date: 1999/12/12 Raw View
Biju Thomas wrote:
>
> Darin Adler wrote:
....
> > 4) Is there a guarantee that algorithms in the standard library will
> > call swap without an explicit "std::" qualifier?
>
> There is a footnote under section 17.4.3.1 (163) saying that library
> implementations must be prepared to work with specializations of the
> templates in the standard library. So, I think you don't have to worry
> about this.
Yes - but a specialization of a template must be in the same namespace
as the template itself. You can't partially specialize a template
function. The closest equivalent to partial specialization for template
functions is partial ordering of overloaded template functions, and the
user is not allowed to add new function templates to namespace std.
There's no guarantee that standard library code uses swap() rather than
std::swap().
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]