While implementing a custom tuple
(here), I found there is a wired swap()
function that takes const
parameters (cppreference):
template< class... Types >
constexpr void swap( const std::tuple<Types...>& lhs,
const std::tuple<Types...>& rhs ) noexcept(/* see below */);
and a const-qualified swap()
member function (cppreference):
constexpr void swap( const tuple& other ) noexcept(/* see below */) const;
const
means the object is read-only, but to swap two objects, it has to modify the objects, which violates the const
-ness.
So, What's the purpose of const swap() function?
CodePudding user response:
This was introduced in the "zip" proposal P2321 originally described in "A Plan for C 23 Ranges" P2214.
P2321
swap for const tuple and const pair. Once tuples of references are made const-assignable, the default std::swap can be called for const tuples of references. However, that triple-move swap does the wrong thing:
int i = 1, j = 2; const auto t1 = std::tie(i), t2 = std::tie(j); // If std::swap(t1, t2); called the default triple-move std::swap then // this would do auto tmp = std::move(t1); t1 = std::move(t2); t2 = std::move(tmp); // i == 2, j == 2
This paper therefore proposes adding overloads of swap for const tuples and pairs to correctly perform element-wise swap.
P2214 explains why const assignability is needed for the implementation of zip
. It stems from assignment operators not being ref qualified.
CodePudding user response:
You have missed the footnote about when that overload is available:
This overload participates in overload resolution only if
std::is_swappable_v<const Ti>
istrue
for all i from 0 tosizeof...(Types)
.
If you have a type const_swappable
such that swap(const const_swappable &, const const_swappable &)
is sensible, then there is no reason why you shouldn't be able to swap const std::tuple<const_swappable> &
.
CodePudding user response:
As an example, consider a pointer-like type, that can swap the values of the pointee without modifying the pointer:
#include <type_traits>
#include <iostream>
struct foo {
int * x;
};
void swap(const foo& a, const foo& b){
std::swap(*a.x,*b.x);
};
int main(){
int a = 42;
int b = 3;
foo f1{&a};
foo f2{&b};
swap(f1,f2);
std::cout << "foo is const swappable: " << std::is_swappable_v<const foo> << "\n";
std::cout << *f1.x << "\n";
std::cout << *f2.x << "\n";
}
And note from cppreference:
- The program is ill-formed if
(std::is_swappable_v<const Types> && ...)
is nottrue
.
That is: You can only const swap the tuples if the types in the tuple can be const swapped.