Topic: Must standard algorithms use qualified names?
Author: "Bill Wade" <bill.wade@stoner.com>
Date: 2000/04/07 Raw View
Lisa Lippincott <lisa_lippincott@bigfix.com> wrote in message
news:p04310105b51026c6b112@[38.168.253.69]...
> (This is a question suggested by an ongoing thread "Namespace issue
> with specialized swap" in comp.lang.c++.moderated.)
>
> Must the standard algorithms use qualified names when referring to
> each other, in order to avoid being hijacked by Koenig lookup?
>
> More concretely, is this a conforming implementation of std::iter_swap?
> (Ignoring the lack of uglification, that is.)
>
> template < class ForwardIterator1, class ForwardIterator2 >
> inline void iter_swap( ForwardIterator1 a, ForwardIterator2 b )
> {
> swap(*a, *b);
> }
>
> In my opinion, it is not. (Sorry, Howard.) It does not produce the
> required effect in this perverse program:
>
> #include <algorithm>
> #include <iostream>
> #include <vector>
>
> enum A { zero, one };
> inline void swap( A&, A& ) {}
>
> [example calls iter_swap]
>
> The algorithm can be corrected by calling std::swap, rather than
> whatever swap Koenig lookup finds. While the standard gives me
> the freedom to be quite perverse in my namespaces, I must behave
> myself when specializing templates in std.
I'd guess that most C++ users (including committee members) have
traditionally expected that a user who wanted a specialized swap() for one
of his classes would define the swap in the same namespace as his class and
algorithms like sort() would use the specialized version.
Recent discussions in boost, then clc++m and here suggest that is not the
case. Before this gets too far let me modify Lisa's example somewhat.
Is this a conforming implementation of ostream_iterator::operator=()?
ostream_iterator<Value, Char, Trait>&
ostream_iterator<Value, Char, Trait>::operator=(const Value& x)
{
*m_stream << x; // Look at this line
if (m_delim)
*m_stream << m_delim;
return (*this);
};
A second implementation might change the commented line to
std::operator<<(*m_stream, x);
A program can tell which implementation it got with
namespace not_std
{
class A
{
public:
operator int() const { return 1; }
};
// I know this should be templated with basic_ostream, but I am lazy
ostream& operator<<(ostream& os, const A& /* not used */){ return os <<
"2"; }
}
So if you are going to argue that standard implementations should use
qualified names when calling things like swap() (and by extension op<<()),
you should also argue that every book that showed people how to write op<<()
for their UDT's is wrong. People should be putting their custom op<<() in
namespace std.
I'm not real happy with the implications of any of the potential answers to
the Subject: question.
a) std::functions must call std::functions. Means people have to put their
op<< in namespace std. Ugly and counter-intuitive. Seems to defeat many of
the advantages of Koenig lookup.
b) std::functions must make unqualified calls. Means that the semantics
associated with std::names effectively pollute all namespaces.
c) Implementations can do what they want. Users get the worst of both
worlds.
Until the committee reaches a decision and implementations catch up with
that decision, it would seem that users are stuck with (c).
---
[ 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: Lisa Lippincott <lisa_lippincott@bigfix.com>
Date: 2000/04/05 Raw View
(This is a question suggested by an ongoing thread "Namespace issue
with specialized swap" in comp.lang.c++.moderated.)
Must the standard algorithms use qualified names when referring to
each other, in order to avoid being hijacked by Koenig lookup?
More concretely, is this a conforming implementation of std::iter_swap?
(Ignoring the lack of uglification, that is.)
template < class ForwardIterator1, class ForwardIterator2 >
inline void iter_swap( ForwardIterator1 a, ForwardIterator2 b )
{
swap(*a, *b);
}
In my opinion, it is not. (Sorry, Howard.) It does not produce the
required effect in this perverse program:
#include <algorithm>
#include <iostream>
#include <vector>
enum A { zero, one };
inline void swap( A&, A& ) {}
int main()
{
std::vector<A> v;
v.push_back( zero );
v.push_back( one );
std::iter_swap( v.begin(), v.begin()+1 );
std::cout << ( v[0] == one ? "correct" : "incorrect" )
<< std::endl;
}
The algorithm can be corrected by calling std::swap, rather than
whatever swap Koenig lookup finds. While the standard gives me
the freedom to be quite perverse in my namespaces, I must behave
myself when specializing templates in std.
Here's a related question: is this a conforming implementation of
std::swap_ranges?
template < class ForwardIterator1, class ForwardIterator2 >
inline ForwardIterator2
swap_ranges( ForwardIterator1 first1, ForwardIterator1 last1,
ForwardIterator2 first2 )
{
for ( ; first1 != last1; ++first1, ++first2 )
swap( *first1, *first2 );
return first2;
}
The question here is more subtle, because the standard defines
swap_ranges in terms of swap:
Effects: For each non-negative integer n < (last1 - first1)
performs: swap( *(first1 + n), *(first2 + n) ).
I think that one can argue that the implementation above does
indeed conform; in my opinion, the success of that argument
indicates a defect in the standard.
--Lisa Lippincott
---
[ 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 ]