From libstdc++ <concepts> header:
namespace ranges
{
namespace __cust_swap
{
template<typename _Tp> void swap(_Tp&, _Tp&) = delete;
From MS-STL <concepts> header:
namespace ranges {
namespace _Swap {
template <class _Ty>
void swap(_Ty&, _Ty&) = delete;
I've never encountered = delete; outside the context where you want to prohibit the call to copy/move assignment/ctor.
I was curious if this was necessary, so I've commented out the = delete; part from the library like this:
// template<typename _Tp> void swap(_Tp&, _Tp&) = delete;
to see if the following test case compiles.
#include <concepts>
#include <iostream>
struct dummy {
friend void swap(dummy& a, dummy& b) {
std::cout << "ADL" << std::endl;
}
};
int main()
{
int a{};
int b{};
dummy c{};
dummy d{};
std::ranges::swap(a, b);
std::ranges::swap(c, d); // Ok. Prints "ADL" on console.
}
Not only it compiles, it seems to behave well by calling user defined swap for struct dummy. So I'm wondering,
- What does
template<typename _Tp> void swap(_Tp&, _Tp&) = delete;exactly do in this context? - On what occasion does this break without
template<typename _Tp> void swap(_Tp&, _Tp&) = delete;?
TL;DR: It's there to keep from calling
std::swap.This is actually an explicit requirement of the
ranges::swapcustomization point:So what does this do? To understand the point of this, we have to remember that the
rangesnamespace is actually thestd::rangesnamespace. That's important because a lot of stuff lives in thestdnamespace. Including this, declared in<utility>:There's probably a
constexprandnoexcepton there somewhere, but that's not relevant for our needs.std::ranges::swap, as a customization point, has a specific way it wants you to customize it. It wants you to provide aswapfunction that can be found via argument-dependent lookup. Which means thatranges::swapis going to find your swap function by doing this:swap(E1, E2).That's fine, except for one problem:
std::swapexists. In the pre-C++20 days, one valid way of making a type swappable was to provide a specialization for thestd::swaptemplate. So if you calledstd::swapdirectly to swap something, your specializations would be picked up and used.ranges::swapdoes not want to use those. It has one customization mechanism, and it wants you to very definitely use that mechanism, not template specialization ofstd::swap.However, because
std::ranges::swaplives in thestdnamespace, unqualified calls toswap(E1, E2)can findstd::swap. To avoid finding and using this overload, it poisons the overload by making visible a version that is= deleted. So if you don't provide an ADL-visibleswapfor your type, you get a hard error. A proper customization is also required to be more specialized (or more constrained) than thestd::swapversion, so that it can be considered a better overload match.Note that
ranges::begin/endand similar functions have similar wording to shut down similar problems with similarly namedstd::functions.