[Variant] [BGL] Variant<edge_descriptor, edge_descriptor> is not EqualityComparable

Hi to all, this discussion is linked to "Boost.Variant is not EqualityComparable". I've opened a new one because to involve the BGL argument. The problem is simply this. I've created a union graph adaptor, that provides a "union view" of two graphs; in that adaptor, the edge_descriptor is defined like this: typedef boost::variant< WR1<typename graph_traits<Graph1>::edge_descriptor>, WR2<typename graph_traits<Graph2>::edge_descriptor> > edge_descriptor; Now I would to use that graph to the Dijkstra's algorithm, that requires that the edge_descriptor models the EqualityComparable concept. But as we know, boost::variant doesn't support the inequality expression ( v1 != v2 ). I've tried to add it from my code (I would avoid to modify the boost lib), but it doesn't works. I'm using MSVS 2005 on WinXP, boost 1.38. I've tried to add the following line, as suggested by Stefan Strasser (thanks Stefan): using namespace std::rel_ops; to add the generic operator!=(), but the compiler doesn't find it, the compiler error is the same. Now I try to provide to you more details. If it isn't enough, I can provide the entire code. I've defined the graph adaptor like this: ////// union_graph.hpp ////// #include <boost/graph/graph_traits.hpp> #include <boost/graph/properties.hpp> #include <boost/graph/adjacency_iterator.hpp> #include <boost/graph/graph_utility.hpp> #include <boost/graph/exception.hpp> #include <boost/variant.hpp> namespace boost { template <typename T> class WR1 { /* ... */ }; template <typename T> class WR2 { /* ... */ }; template <typename Graph1, typename Graph2> class union_graph { public: typedef Graph1 graph_type1; typedef Graph2 graph_type2; // Constructor union_graph( Graph1& g1, Graph2& g2 ) : m_g1(g1), m_g2(g2) { /* ... */ } // Graph requirements typedef boost::variant< WR1<typename graph_traits<Graph1>::vertex_descriptor>, WR2<typename graph_traits<Graph2>::vertex_descriptor> > vertex_descriptor; /* ... */ // IncidenceGraph requirements typedef boost::variant< WR1<typename graph_traits<Graph1>::edge_descriptor>, WR2<typename boost::graph_traits<Graph2>::edge_descriptor> > edge_descriptor; /* ... */ }; // EqualityComparable concept, requested by IncidenceGraph concept template <typename Graph1, typename Graph2> inline bool operator!=( const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& left, const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& right ) { return !( left == right ); } and I've used it like this: ////// main.cpp ////// #include "union_graph.hpp" int main() { typedef adjacency_list<...> Graph1; typedef adjacency_list<...> Graph2; typedef union_graph<Graph1, Graph2> UG; Graph1 g1( 10 ); Graph1 g2( 10 ); UG ug( g1, g2 ); graph_traits<UG>::edge_descriptor e1; graph_traits<UG>::edge_descriptor e2; tie( e1, existence ) = edge( v1, v2, g ); tie( e2, existence ) = edge( v1, v2, g ); e1 != e2; } The first part of the template compiler error is: 1>D:\dev\CommesseAperte\Sviluppo\TELLUS36_Librerie\boost_1_38_0\boost/concept_check.hpp(233) : error C2678: binary '!=' : no operator found which takes a left-hand operand of type 'boost::variant<T0_,T1>' (or there is no acceptable conversion) 1> with 1> [ 1> T0_=boost::WR1<boost::detail::edge_desc_impl<boost::directed_tag,__w64 unsigned int>>, 1> T1=boost::WR2<boost::detail::edge_desc_impl<boost::directed_tag,__w64 unsigned int>> 1> ] Can someone help me? I wouldn't hack the lib, because I will do it to every new version of Boost... Thanks in advance, Cosimo Calabrese.

// EqualityComparable concept, requested by IncidenceGraph concept template <typename Graph1, typename Graph2> inline bool operator!=( const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& left, const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& right ) { return !( left == right ); }
UG ug( g1, g2 );
graph_traits<UG>::edge_descriptor e1; graph_traits<UG>::edge_descriptor e2;
tie( e1, existence ) = edge( v1, v2, g ); tie( e2, existence ) = edge( v1, v2, g );
e1 != e2; }
I looks like the inequality operator is provided in terms of edge descriptors, but you're actually comparing variants. You may be able to get away with writing something like: template <typename G1, typename G2> bool operator!=( typename graph_traits<union_graph<G1, G2>>::edge_descriptor left, typename graph_traits<union_graph<G1, G2>>::edge_descriptor right) { ... } That should generate an inequality operator over variants, but prevent the template from becoming overly general, since its explicitly "specialized" for your union_graph. Maybe? Andrew Sutton andrew.n.sutton@gmail.com

Andrew Sutton wrote:
// EqualityComparable concept, requested by IncidenceGraph concept template <typename Graph1, typename Graph2> inline bool operator!=( const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& left, const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& right ) { return !( left == right ); }
UG ug( g1, g2 );
graph_traits<UG>::edge_descriptor e1; graph_traits<UG>::edge_descriptor e2;
tie( e1, existence ) = edge( v1, v2, g ); tie( e2, existence ) = edge( v1, v2, g );
e1 != e2; }
I looks like the inequality operator is provided in terms of edge descriptors, but you're actually comparing variants. You may be able to get away with writing something like:
template <typename G1, typename G2> bool operator!=( typename graph_traits<union_graph<G1, G2>>::edge_descriptor left, typename graph_traits<union_graph<G1, G2>>::edge_descriptor right) { ... }
That should generate an inequality operator over variants, but prevent the template from becoming overly general, since its explicitly "specialized" for your union_graph.
Maybe?
Andrew Sutton
I'm in agreement with you, but that code doesn't work. I don't know why, I've tried it yesterday. This is my version. I've written it in the global namespace, not in boost namespace: template <typename G1, typename G2> bool operator!=( typename boost::graph_traits<boost::union_graph<G1, G2>
::edge_descriptor left, typename boost::graph_traits<boost::union_graph<G1, G2> >::edge_descriptor right ) { return !( left == right ); }
Instead it works the follow, more general: template <typename T0_, typename T1> inline bool operator!=( const boost::variant<T0_, T1>& left, const boost::variant<T0_, T1>& right ) { return !( left == right ); } I've written it in the same position of the first version, but it provides the operator!=() for all the variant specialization... What do you think about? Cheers, Cosimo Calabrese.

Cosimo Calabrese wrote:
Andrew Sutton wrote:
// EqualityComparable concept, requested by IncidenceGraph concept template <typename Graph1, typename Graph2> inline bool operator!=( const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& left, const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& right ) { return !( left == right ); }
UG ug( g1, g2 );
graph_traits<UG>::edge_descriptor e1; graph_traits<UG>::edge_descriptor e2;
tie( e1, existence ) = edge( v1, v2, g ); tie( e2, existence ) = edge( v1, v2, g );
e1 != e2; }
I looks like the inequality operator is provided in terms of edge descriptors, but you're actually comparing variants. You may be able to get away with writing something like:
template <typename G1, typename G2> bool operator!=( typename graph_traits<union_graph<G1, G2>>::edge_descriptor left, typename graph_traits<union_graph<G1, G2>>::edge_descriptor right) { ... }
That should generate an inequality operator over variants, but prevent the template from becoming overly general, since its explicitly "specialized" for your union_graph.
Maybe?
Andrew Sutton
I'm in agreement with you, but that code doesn't work. I don't know why, I've tried it yesterday. This is my version. I've written it in the global namespace, not in boost namespace:
template <typename G1, typename G2> bool operator!=( typename boost::graph_traits<boost::union_graph<G1, G2>
::edge_descriptor left, typename boost::graph_traits<boost::union_graph<G1, G2> ::edge_descriptor right ) { return !( left == right ); }
Instead it works the follow, more general:
template <typename T0_, typename T1> inline bool operator!=( const boost::variant<T0_, T1>& left, const boost::variant<T0_, T1>& right ) { return !( left == right ); }
I've written it in the same position of the first version, but it provides the operator!=() for all the variant specialization...
What do you think about?
I've tried to make esplicit the edge_descriptor definition, without using graph_traits class: template <typename G1, typename G2> bool operator!=( boost::variant< boost::WR1<typename boost::graph_traits<G1>::edge_descriptor>, boost::WR2<typename boost::graph_traits<G2>::edge_descriptor> >& left, boost::variant< boost::WR1<typename boost::graph_traits<G1>::edge_descriptor>, boost::WR2<typename boost::graph_traits<G2>::edge_descriptor> >& right ) { return !( left == right ); } but doesn't work, the compile doesn't resolve it. But this works: template <typename T1, typename T2> bool operator!=( boost::variant<boost::WR1<T1>, boost::WR2<T2> >& left, boost::variant<boost::WR1<T1>, boost::WR2<T2> >& right ) { return !( left == right ); } It is less general that the template <typename T0_, typename T1> version, but it involves the vertex_description too. It's make me crazy... Thanks, Cosimo Calabrese.

Cosimo Calabrese wrote:
Cosimo Calabrese wrote:
Andrew Sutton wrote:
// EqualityComparable concept, requested by IncidenceGraph concept template <typename Graph1, typename Graph2> inline bool operator!=( const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& left, const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& right ) { return !( left == right ); }
UG ug( g1, g2 );
graph_traits<UG>::edge_descriptor e1; graph_traits<UG>::edge_descriptor e2;
tie( e1, existence ) = edge( v1, v2, g ); tie( e2, existence ) = edge( v1, v2, g );
e1 != e2; }
I looks like the inequality operator is provided in terms of edge descriptors, but you're actually comparing variants. You may be able to get away with writing something like:
template <typename G1, typename G2> bool operator!=( typename graph_traits<union_graph<G1, G2>>::edge_descriptor left, typename graph_traits<union_graph<G1, G2>>::edge_descriptor right) { ... }
That should generate an inequality operator over variants, but prevent the template from becoming overly general, since its explicitly "specialized" for your union_graph.
Maybe?
Andrew Sutton
I'm in agreement with you, but that code doesn't work. I don't know why, I've tried it yesterday. This is my version. I've written it in the global namespace, not in boost namespace:
template <typename G1, typename G2> bool operator!=( typename boost::graph_traits<boost::union_graph<G1, G2> >::edge_descriptor left, typename boost::graph_traits<boost::union_graph<G1, G2> >::edge_descriptor right ) { return !( left == right ); }
Instead it works the follow, more general:
template <typename T0_, typename T1> inline bool operator!=( const boost::variant<T0_, T1>& left, const boost::variant<T0_, T1>& right ) { return !( left == right ); }
I've written it in the same position of the first version, but it provides the operator!=() for all the variant specialization...
What do you think about?
I've tried to make esplicit the edge_descriptor definition, without using graph_traits class:
template <typename G1, typename G2> bool operator!=( boost::variant< boost::WR1<typename boost::graph_traits<G1>::edge_descriptor>, boost::WR2<typename boost::graph_traits<G2>::edge_descriptor> >& left, boost::variant< boost::WR1<typename boost::graph_traits<G1>::edge_descriptor>, boost::WR2<typename boost::graph_traits<G2>::edge_descriptor> >& right ) { return !( left == right ); }
but doesn't work, the compile doesn't resolve it.
But this works:
template <typename T1, typename T2> bool operator!=( boost::variant<boost::WR1<T1>, boost::WR2<T2> >& left, boost::variant<boost::WR1<T1>, boost::WR2<T2> >& right ) { return !( left == right ); }
It is less general that the template <typename T0_, typename T1> version, but it involves the vertex_description too.
It's make me crazy...
Thanks, Cosimo Calabrese.
I've tried to write an output stream operator for vertex_descriptor, but it happens the same thing. This doesn't works: namespace std { template <typename Graph1, typename Graph2> std::ostream& operator<<(std::ostream& os, const typename boost::graph_traits<boost::union_graph<Graph1, Graph2> >::vertex_descriptor& v) { if ( boost::get<WR1<graph_traits<Graph1>::vertex_descriptor> >( &v ) ) os << boost::get<WR1<graph_traits<Graph1>::vertex_descriptor>
(v)._data; else os << boost::get<WR2<graph_traits<Graph2>::vertex_descriptor> >(v)._data; return os; } }
but this works: namespace std { template <typename T1, typename T2> std::ostream& operator<<(std::ostream& os, const boost::variant<boost::WR1<T1>, boost::WR2<T2> >& v) { if ( boost::get<boost::WR1<T1> >( &v ) ) os << boost::get<WR1<T1> >( v )._data; else os << boost::get<WR2<T2> >( v )._data; return os; } } It seems the same case. Cosimo Calabrese.

but this works:
namespace std
{ template <typename T1, typename T2> std::ostream& operator<<(std::ostream& os, const boost::variant<boost::WR1<T1>, boost::WR2<T2> >& v) { if ( boost::get<boost::WR1<T1> >( &v ) ) os << boost::get<WR1<T1> >( v )._data; else os << boost::get<WR2<T2> >( v )._data; return os; } }
It seems the same case.
This seems to be a SFINAE problem. Is it possible, that graph_traits<union_graph<...>>::vertex_descriptor or edge_descriptor does not name an actual type? For example, did you misspell the type names inside the union_graph class? I've had that problem before (it took an hour to figure it out). There's also a possibility that the compiler is choking on the complexity of the parameterization. You are using a slightly dated compiler. Any chance you can upgrade? Andrew Sutton andrew.n.sutton@gmail.com

Andrew Sutton wrote:
but this works:
namespace std
{ template <typename T1, typename T2> std::ostream& operator<<(std::ostream& os, const boost::variant<boost::WR1<T1>, boost::WR2<T2> >& v) { if ( boost::get<boost::WR1<T1> >( &v ) ) os << boost::get<WR1<T1> >( v )._data; else os << boost::get<WR2<T2> >( v )._data; return os; } }
It seems the same case.
Thanks Andrew,
This seems to be a SFINAE problem. Is it possible, that
I've never seen SFINAE before, so I've spent a little on it. It seems the case in which I've more of one possibility of overload, that can cause ambiguity. In my case it seems I've no possibility, because there isn't any compatibility between my operator and the call. Another real possibility is that I haven't understand SFINAE :)
graph_traits<union_graph<...>>::vertex_descriptor or edge_descriptor does not name an actual type?
If I specify Graph1 and Graph2, the descriptors are defined. I don't know if it happens in my case. But after reading the Stefan reply, I'm not sure of it...
For example, did you misspell the type names inside the union_graph class? I've had that problem before (it took an hour to figure it out).
Well, I've reported it in the first post. The definition of the edge_descriptor is: template <typename Graph1, typename Graph2> class union_graph { public: /* ... */ typedef boost::variant< WR1<typename graph_traits<Graph1>::edge_descriptor>, WR2<typename graph_traits<Graph2>::edge_descriptor> > edge_descriptor; /* ... */ }; and the operator definition is: template <typename Graph1, typename Graph2> inline bool operator!=( const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& left, const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& right ); So the edge_descriptor definition and the operator arguments type should correspond. Now I think that the problem is in the call of operator.
There's also a possibility that the compiler is choking on the complexity of the parameterization. You are using a slightly dated compiler. Any chance you can upgrade?
I've tried on MSVS 2008, but the result is the same. Thank you for help, Cosimo Calabrese

I've never seen SFINAE before, so I've spent a little on it. It seems the case in which I've more of one possibility of overload, that can cause ambiguity. In my case it seems I've no possibility, because there isn't any compatibility between my operator and the call.
It's the formal name for the problems that have been described. You can't find an overload because, somehow, the compiler is rejecting your template.
I've tried on MSVS 2008, but the result is the same.
If you can post your full implementation, I'll run it through GCC and see if the error persists, or if there's a nicer way to fix it. Andrew Sutton andrew.n.sutton@gmail.com

If you can post your full implementation, I'll run it through GCC and see if the error persists, or if there's a nicer way to fix it.
In my previous post I've attached the "workaround" code. If you want to replicate the problem, please substitute the following code in union_graph.hpp: template <typename T1, typename T2> bool operator!=( variant<WR1<T1>, WR2<T2> >& left, variant<WR1<T1>, WR2<T2> >& right ) { return !( left == right ); } with my previous version of operator!=() template <typename Graph1, typename Graph2> inline bool operator!=( const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& left, const typename boost::graph_traits< boost::union_graph<Graph1, Graph2> >::edge_descriptor& right ) { return !( left == right ); } Cosimo Calabrese

If you can post your full implementation, I'll run it through GCC and see
if the error persists, or if there's a nicer way to fix it.
For starters, the program you gave me doesn't compile in GCC - actually, it probably shouldn't compile at all. Some of your functions in Test_union_different_iterator are defined over type parameters when they need to be defined over template template parameters. See attached for fixes. The good news is that GCC will generate the same compiler error. I /think/ the problem is related to the fact that variant actually defines operator== as a member function. This seems to cause the compiler to "prefer" a member operator!=, which variant does not define. Maybe the overload you gave is somehow incompatible with that. One of my students reported a similar issue on a different project. Best bet: stick with the solution you have. Andrew Sutton andrew.n.sutton@gmail.com

Andrew Sutton wrote:
If you can post your full implementation, I'll run it through GCC and see
if the error persists, or if there's a nicer way to fix it.
For starters, the program you gave me doesn't compile in GCC - actually, it probably shouldn't compile at all. Some of your functions in Test_union_different_iterator are defined over type parameters when they need to be defined over template template parameters. See attached for fixes.
Thanks for the corrections, they are many instructive for me, they are my first iterator and adaptors... Effectively my code runs correctly on MSVS 2005; it seems that GCC is (fortunately) more rigorous.
The good news is that GCC will generate the same compiler error.
Best bet: stick with the solution you have.
Ok! Thank you very much for help me. Cheers, Cosimo Calabrese.

Am Thursday 12 November 2009 15:00:09 schrieb Cosimo Calabrese:
namespace std { template <typename Graph1, typename Graph2> std::ostream& operator<<(std::ostream& os, const typename boost::graph_traits<boost::union_graph<Graph1, Graph2>
>::vertex_descriptor& v)
{ if ( boost::get<WR1<graph_traits<Graph1>::vertex_descriptor> >( &v ) ) os << boost::get<WR1<graph_traits<Graph1>::vertex_descriptor>
(v)._data;
else os << boost::get<WR2<graph_traits<Graph2>::vertex_descriptor> >(v)._data; return os; } }
the template parameters (Graph1/2) must be deducable from the function parameter (v). this is not possible in this example. consider the following, simpler, case: template<class T> struct A{ typedef T type; }; template<class A_T> void func(typename A_T::type); int main(){ int a; func<A<int> >(); //ok func(a); //error } the compiler has no way of knowing that foo is supposed to be instantiated with A_T == A, unless you provide an explicit template argument (which you don't when you call an operator).

Stefan Strasser ha scritto:
Am Thursday 12 November 2009 15:00:09 schrieb Cosimo Calabrese:
namespace std { template <typename Graph1, typename Graph2> std::ostream& operator<<(std::ostream& os, const typename boost::graph_traits<boost::union_graph<Graph1, Graph2>
>::vertex_descriptor& v)
{ if ( boost::get<WR1<graph_traits<Graph1>::vertex_descriptor> >( &v ) ) os << boost::get<WR1<graph_traits<Graph1>::vertex_descriptor>
(v)._data;
else os << boost::get<WR2<graph_traits<Graph2>::vertex_descriptor> >(v)._data; return os; } }
the template parameters (Graph1/2) must be deducable from the function parameter (v). this is not possible in this example.
consider the following, simpler, case: template<class T> struct A{ typedef T type; };
template<class A_T> void func(typename A_T::type);
int main(){ int a; func<A<int> >(); //ok func(a); //error }
the compiler has no way of knowing that foo is supposed to be instantiated with A_T == A, unless you provide an explicit template argument (which you don't when you call an operator). _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Sorry for late, but I have had to think on a little... So, IIUC there is a concept required by the A_T type, that is A_T must has a "child" type (A_T::type). The argument of func is the "child" of A_T. It's crucial to know which is the actual A_T type to istantiate the correct func version. So, if I explicitly specify the template parameter, e.g.: func<A<int> >(...); so the correct func version is istantiated, and this specifies that the func actual argument is an int. But if I don't specify the template param and pass an int as argument, the compiler can't know which is the parent of that int. I've tried to create a variable of type: A<int>::type obj; and pass it to func, but the compiler can't deduce the template argument; perhaps obj is just an int, and the fact that the int is a A<int>::type is ignored. So the error in my case is not in the prototype declaration of operator<<() or operator!=(); the error is in the call of operators. To call correctly the operators I should (theoretically) call: boost::graph_traits< boost::union_graph<ActualGraph1, ActualGraph2>
::edge_descriptor a, b;
// a, b assignment if ( operator!= <ActualGraph1, ActualGraph2>( a, b ) ) { //... } but: - it's illeagal; - a generic algorithm that applies operator!=() can't know the actual type of Graph1 and Graph2. Is it correct? So, is the problem originated by the fact that the function argument is a "child" of the template param type? And in my case, the only thing I can do is avoid this? Like: template <typename T1, typename T2> bool operator!=( boost::variant<boost::WR1<T1>, boost::WR2<T2> >& left, boost::variant<boost::WR1<T1>, boost::WR2<T2> >& right) { return !( left == right ); } in that WR1<T1> is not a "child" of T1, but an actual type that depends by T1? Thanks, Cosimo Calabrese.

Am Thursday 12 November 2009 12:05:33 schrieb Cosimo Calabrese:
using namespace std::rel_ops;
to add the generic operator!=(), but the compiler doesn't find it, the compiler error is the same.
using namespace std::rel_ops; (or your free function, it's the same thing) needs to be before the point operator != is used. I didn't realize this because GCC accepts it if it's there at the point of instantiation, but other compilers don't. so try to put it at the beginning of the file, before including the graph library. this is only a workaround though. the graph library shouldn't use operator!= if its only requirement on the type is EqualityComparable. have you filed a bug ticket?

On Thu, 12 Nov 2009, Stefan Strasser wrote:
Am Thursday 12 November 2009 12:05:33 schrieb Cosimo Calabrese:
using namespace std::rel_ops;
to add the generic operator!=(), but the compiler doesn't find it, the compiler error is the same.
using namespace std::rel_ops; (or your free function, it's the same thing) needs to be before the point operator != is used. I didn't realize this because GCC accepts it if it's there at the point of instantiation, but other compilers don't. so try to put it at the beginning of the file, before including the graph library.
this is only a workaround though. the graph library shouldn't use operator!= if its only requirement on the type is EqualityComparable. have you filed a bug ticket?
operator!= is required by EqualityComparable, at least according to the SGI STL and Boost.ConceptCheck requirements. -- Jeremiah Willcock

Am Thursday 12 November 2009 15:41:11 schrieb Jeremiah Willcock:
On Thu, 12 Nov 2009, Stefan Strasser wrote:
Am Thursday 12 November 2009 12:05:33 schrieb Cosimo Calabrese:
using namespace std::rel_ops;
to add the generic operator!=(), but the compiler doesn't find it, the compiler error is the same.
using namespace std::rel_ops; (or your free function, it's the same thing) needs to be before the point operator != is used. I didn't realize this because GCC accepts it if it's there at the point of instantiation, but other compilers don't. so try to put it at the beginning of the file, before including the graph library.
this is only a workaround though. the graph library shouldn't use operator!= if its only requirement on the type is EqualityComparable. have you filed a bug ticket?
operator!= is required by EqualityComparable, at least according to the SGI STL and Boost.ConceptCheck requirements.
as discussed in the other thread, I think SGI is either mistaken or outdated here. a lot of SGI STL was written before C++ was standardized and some stuff was kept for compatibility. in this draft of the standard from 2005(!) there is no such requirement: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf see page 421

Stefan Strasser wrote:
as discussed in the other thread, I think SGI is either mistaken or outdated here. a lot of SGI STL was written before C++ was standardized and some stuff was kept for compatibility. in this draft of the standard from 2005(!) there is no such requirement: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf
see page 421
And in N2723 from 2008-08-25 the wording is identical. There's no requirement for != Patrick
participants (5)
-
Andrew Sutton
-
Cosimo Calabrese
-
Jeremiah Willcock
-
Patrick Horgan
-
Stefan Strasser