libstdc++: Define operator== for hash table iterators [PR115939]
Currently iterators for unordered containers do not directly define operator== and operator!= overloads. Instead they rely on the base class defining them, which is done so that iterator and const_iterator comparisons work using the same overloads. However this means a derived-to-base conversion is needed to call those operators, and PR libstdc++/115939 shows that this can be ambiguous (for -pedantic) when another overloaded operator could be used after an implicit conversion. This change defines operator== and operator!= directly for _Node_iterator and _Node_const_iterator so that no derived-to-base conversions are needed. The new overloads just forward to the base class ones, so the implementation is still shared and doesn't need to be duplicated. libstdc++-v3/ChangeLog: PR libstdc++/115939 * include/bits/hashtable_policy.h (_Node_iterator): Add operator== and operator!=. (_Node_const_iterator): Likewise. * testsuite/23_containers/unordered_map/115939.cc: New test.
This commit is contained in:
parent
125bab23ad
commit
591b71993f
2 changed files with 107 additions and 2 deletions
|
@ -464,6 +464,23 @@ namespace __detail
|
|||
this->_M_incr();
|
||||
return __tmp;
|
||||
}
|
||||
|
||||
#if __cpp_impl_three_way_comparison >= 201907L
|
||||
friend bool
|
||||
operator==(const _Node_iterator&, const _Node_iterator&) = default;
|
||||
#else
|
||||
friend bool
|
||||
operator==(const _Node_iterator& __x, const _Node_iterator& __y) noexcept
|
||||
{
|
||||
const __base_type& __bx = __x;
|
||||
const __base_type& __by = __y;
|
||||
return __bx == __by;
|
||||
}
|
||||
|
||||
friend bool
|
||||
operator!=(const _Node_iterator& __x, const _Node_iterator& __y) noexcept
|
||||
{ return !(__x == __y); }
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Node const_iterators, used to iterate through all the hashtable.
|
||||
|
@ -475,6 +492,10 @@ namespace __detail
|
|||
using __base_type = _Node_iterator_base<_Value, __cache>;
|
||||
using __node_type = typename __base_type::__node_type;
|
||||
|
||||
// The corresponding non-const iterator.
|
||||
using __iterator
|
||||
= _Node_iterator<_Value, __constant_iterators, __cache>;
|
||||
|
||||
public:
|
||||
typedef _Value value_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
|
@ -489,8 +510,7 @@ namespace __detail
|
|||
_Node_const_iterator(__node_type* __p) noexcept
|
||||
: __base_type(__p) { }
|
||||
|
||||
_Node_const_iterator(const _Node_iterator<_Value, __constant_iterators,
|
||||
__cache>& __x) noexcept
|
||||
_Node_const_iterator(const __iterator& __x) noexcept
|
||||
: __base_type(__x._M_cur) { }
|
||||
|
||||
reference
|
||||
|
@ -515,6 +535,62 @@ namespace __detail
|
|||
this->_M_incr();
|
||||
return __tmp;
|
||||
}
|
||||
|
||||
#if __cpp_impl_three_way_comparison >= 201907L
|
||||
friend bool
|
||||
operator==(const _Node_const_iterator&,
|
||||
const _Node_const_iterator&) = default;
|
||||
|
||||
friend bool
|
||||
operator==(const _Node_const_iterator& __x, const __iterator& __y)
|
||||
{
|
||||
const __base_type& __bx = __x;
|
||||
const __base_type& __by = __y;
|
||||
return __bx == __by;
|
||||
}
|
||||
#else
|
||||
friend bool
|
||||
operator==(const _Node_const_iterator& __x,
|
||||
const _Node_const_iterator& __y) noexcept
|
||||
{
|
||||
const __base_type& __bx = __x;
|
||||
const __base_type& __by = __y;
|
||||
return __bx == __by;
|
||||
}
|
||||
|
||||
friend bool
|
||||
operator!=(const _Node_const_iterator& __x,
|
||||
const _Node_const_iterator& __y) noexcept
|
||||
{ return !(__x == __y); }
|
||||
|
||||
friend bool
|
||||
operator==(const _Node_const_iterator& __x,
|
||||
const __iterator& __y) noexcept
|
||||
{
|
||||
const __base_type& __bx = __x;
|
||||
const __base_type& __by = __y;
|
||||
return __bx == __by;
|
||||
}
|
||||
|
||||
friend bool
|
||||
operator!=(const _Node_const_iterator& __x,
|
||||
const __iterator& __y) noexcept
|
||||
{ return !(__x == __y); }
|
||||
|
||||
friend bool
|
||||
operator==(const __iterator& __x,
|
||||
const _Node_const_iterator& __y) noexcept
|
||||
{
|
||||
const __base_type& __bx = __x;
|
||||
const __base_type& __by = __y;
|
||||
return __bx == __by;
|
||||
}
|
||||
|
||||
friend bool
|
||||
operator!=(const __iterator& __x,
|
||||
const _Node_const_iterator& __y) noexcept
|
||||
{ return !(__x == __y); }
|
||||
#endif
|
||||
};
|
||||
|
||||
// Many of class template _Hashtable's template parameters are policy
|
||||
|
|
29
libstdc++-v3/testsuite/23_containers/unordered_map/115939.cc
Normal file
29
libstdc++-v3/testsuite/23_containers/unordered_map/115939.cc
Normal file
|
@ -0,0 +1,29 @@
|
|||
// { dg-options "-pedantic" }
|
||||
// { dg-do compile { target c++11 } }
|
||||
|
||||
// Bug 115939 - Cannot unambiguously compare iterators in std::unordered_map
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
struct any_conv
|
||||
{
|
||||
template<typename T>
|
||||
any_conv(T&&) { }
|
||||
|
||||
template<typename T>
|
||||
friend bool operator==(const any_conv&, const T&) { return true; }
|
||||
|
||||
template<typename T>
|
||||
friend bool operator!=(const any_conv&, const T&) { return false; }
|
||||
};
|
||||
|
||||
std::unordered_map<int, any_conv>::iterator i{};
|
||||
std::unordered_map<int, any_conv>::const_iterator j{};
|
||||
bool b1 = i == i; // { dg-bogus "ambiguous" }
|
||||
bool b2 = j == j; // { dg-bogus "ambiguous" }
|
||||
bool b3 = i == j; // { dg-bogus "ambiguous" }
|
||||
bool b4 = j == i; // { dg-bogus "ambiguous" }
|
||||
bool b5 = i != i; // { dg-bogus "ambiguous" }
|
||||
bool b6 = j != j; // { dg-bogus "ambiguous" }
|
||||
bool b7 = i != j; // { dg-bogus "ambiguous" }
|
||||
bool b8 = j != i; // { dg-bogus "ambiguous" }
|
Loading…
Add table
Reference in a new issue