Make associative container operators inline friend
François Dumont
frs.dumont@gmail.com
Tue Oct 2 20:55:00 GMT 2018
On 09/28/2018 01:51 PM, Jonathan Wakely wrote:
> Please remember to CC the gcc-patches list for all patches.
>
> On 27/09/18 22:38 +0200, François Dumont wrote:
>> Hi
>>
>> Â Â Â Here is a patch to make all associative containers operators
>> inline friends. This is for both container operators themselves and
>> their associated iterators.
>>
>> Â Â Â Note that I also remove iterator/const_iterator == and !=
>> operators. They are useless as iterator can be implicitly converted
>> into const_iterator so as soon as there is a mix the conversion will
>> take place (and is cheap). Do you see an ABI issue ?
>
> There's no ABI issue, and as these are only internal types I don't see
> any problems that can arise when trying to compare types that are
> convertible to these iterators. So this seems OK.
>
>> Â Â Â I also wonder if Doxygen will properly manage doc on operators I
>> kept when moving those inline.
>
> Yes it should be OK.
>
>> Â Â Â Before I came to this result I faced some problems which make me
>> introduce the _Rb_tree cbegin and cend and use it to avoid sometimes
>> iterator/const_iterator comparisons or conversions. Even if
>> eventually useless I propose to keep it.
>>
>> Â Â Â I also benefit from C++11 syntax simplifications in
>> _GLIBCXX_DEBUG associatives containers implementations.
>
> Those changes to use C++11 syntax in the debug containers are OK, but
> they can be a separate commit from the changes to make operators
> inline friends (and need to be mentioned in the changelog).
Ok I'll commit those soon with proper ChangeLog.
>
>
>> Â Â Â * include/bits/stl_tree.h
>> Â Â Â (_Rb_tree_iterator<>::operator==): Make inline friend.
>> Â Â Â (_Rb_tree_iterator<>::operator!=): Make inline friend.
>> Â Â Â (_Rb_tree_const_iterator<>::operator==): Make inline friend.
>> Â Â Â (_Rb_tree_const_iterator<>::operator!=): Make inline friend.
>> Â Â Â (operator==(const _Rb_tree_iterator<>&,
>> Â Â Â const _Rb_tree_const_iterator&)): Remove.
>> Â Â Â (operator!=(const _Rb_tree_iterator<>&,
>> Â Â Â const _Rb_tree_const_iterator&)): Remove.
>
> These are OK, but ...
>
>> Â Â Â (_Rb_tree<>::cbegin()): New.
>> Â Â Â (_Rb_tree<>::cend()): New.
>
> In C++98/C++03 the names "cbegin" and "cend" are not reserved, so this
> change breaks this valid C++98 program:
>
> #define cbegin 1
> #define cend 1
> #include <set>
> int main() { }
Ok, I reverted this part.
>
>> Â Â Â (operator==(const _Rb_tree<>&, const _Rb_tree<>&)): Make inline
>> friend.
>> Â Â Â (operator!=(const _Rb_tree<>&, const _Rb_tree<>&)): Likewise.
>> Â Â Â (operator<(const _Rb_tree<>&, const _Rb_tree<>&)): Likewise.
>> Â Â Â (operator>(const _Rb_tree<>&, const _Rb_tree<>&)): Likewise.
>> Â Â Â (operator<=(const _Rb_tree<>&, const _Rb_tree<>&)): Likewise.
>> Â Â Â (operator>=(const _Rb_tree<>&, const _Rb_tree<>&)): Likewise.
>
> These changes to _Rb_tree operators are OK, but ...
>
>> Â Â Â * include/bits/stl_map.h
>> Â Â Â (operator==(const map<>&, const map<>&)): Make inline friend.
>> Â Â Â (operator!=(const map<>&, const map<>&)): Likewise.
>> Â Â Â (operator<(const map<>&, const map<>&)): Likewise.
>> Â Â Â (operator>(const map<>&, const map<>&)): Likewise.
>> Â Â Â (operator<=(const map<>&, const map<>&)): Likewise.
>> Â Â Â (operator>=(const map<>&, const map<>&)): Likewise.
>
> These are not. The operators are declared at namespace scope in the
> standard, so we need to also declare them at namespace scope. They
> cannot be hidden friends (this is unfortunate, but it's what the
> standard says).
I consider the Table 83 - Container requirements where it is only saying
that a == b should be well defined without namespace consideration. But
I won't argue on Standard details with you, I'll revert that part.
>
> And it looks like these are missing from the patch anyway:
>
>> Â Â Â * include/bits/stl_set.h
>> Â Â Â (operator==(const set<>&, const set<>&)): Make inline friend.
>> Â Â Â (operator!=(const set<>&, const set<>&)): Likewise.
>> Â Â Â (operator<(const set<>&, const set<>&)): Likewise.
>> Â Â Â (operator>(const set<>&, const set<>&)): Likewise.
>> Â Â Â (operator<=(const set<>&, const set<>&)): Likewise.
>> Â Â Â (operator>=(const set<>&, const set<>&)): Likewise.
>> Â Â Â * include/bits/stl_multiset.h
>> Â Â Â (operator==(const multiset<>&, const multiset<>&)): Make inline
>> friend.
>> Â Â Â (operator!=(const multiset<>&, const multiset<>&)): Likewise.
>> Â Â Â (operator<(const multiset<>&, const multiset<>&)): Likewise.
>> Â Â Â (operator>(const multiset<>&, const multiset<>&)): Likewise.
>> Â Â Â (operator<=(const multiset<>&, const multiset<>&)): Likewise.
>> Â Â Â (operator>=(const multiset<>&, const multiset<>&)): Likewise.
>
>
>> When building attached test case the compiler is proposing 73
>> operator== candidates before the patch and 69 after, respectively 69
>> and 63 in Debug mode.
>
> Wow, that number of overloads is so horrible. Unfortunately, I think
> we can only reduce that number by one :-(
>
>> Ok to commit ?
>
> No, sorry.
>
> The changes to the operators for _Rb_tree and its iterators are OK,
> but see the question below.
>
> The changes to use C++11 syntax in the debug containers are OK.
>
Ok, I'll go with those parts as soon I've completed the cleanup.
>
>
>>
>> François
>>
>
>> diff --git a/libstdc++-v3/include/bits/stl_tree.h
>> b/libstdc++-v3/include/bits/stl_tree.h
>> index 70d7483c7b1..5a0b5b8e9f6 100644
>> --- a/libstdc++-v3/include/bits/stl_tree.h
>> +++ b/libstdc++-v3/include/bits/stl_tree.h
>> @@ -1616,55 +1612,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> Â Â Â Â Â Â Â }
>> Â Â Â Â }
>> #endif // C++17
>> -Â Â Â };
>>
>> -Â template<typename _Key, typename _Val, typename _KeyOfValue,
>> -Â Â Â Â Â Â typename _Compare, typename _Alloc>
>> -Â Â Â inline bool
>> -Â Â Â operator==(const _Rb_tree<_Key, _Val, _KeyOfValue, _Compare,
>> _Alloc>& __x,
>> -Â Â Â Â Â Â Â Â Â Â const _Rb_tree<_Key, _Val, _KeyOfValue, _Compare,
>> _Alloc>& __y)
>> +Â Â Â Â Â friend bool
>> +Â Â Â Â Â operator==(const _Rb_tree& __x, const _Rb_tree& __y)
>> Â Â Â Â Â {
>> Â Â Â Â return __x.size() == __y.size()
>> Â Â Â Â Â && std::equal(__x.begin(), __x.end(), __y.begin());
>> Â Â Â Â Â }
>>
>> -Â template<typename _Key, typename _Val, typename _KeyOfValue,
>> -Â Â Â Â Â Â typename _Compare, typename _Alloc>
>> -Â Â Â inline bool
>> -Â Â Â operator<(const _Rb_tree<_Key, _Val, _KeyOfValue, _Compare,
>> _Alloc>& __x,
>> -Â Â Â Â Â Â Â Â Â const _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&
>> __y)
>> +Â Â Â Â Â friend bool
>> +Â Â Â Â Â operator<(const _Rb_tree& __x, const _Rb_tree& __y)
>> Â Â Â Â Â {
>> Â Â Â Â return std::lexicographical_compare(__x.begin(), __x.end(),
>> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â __y.begin(), __y.end());
>> Â Â Â Â Â }
>
> Why do we even have the operators below?
>
> As far as I can tell, we only every use __x._M_t < __y._M_t and
> __x._M_t == __y._M_t, so don't need anything else.
For sure we don't use those for associative containers operators. I
guess they have been added for consistency with Standard containers.
There are here for a long time, do you want to remove them now ? I can
add this cleanup in my patch if you want.
François
More information about the Libstdc++
mailing list