What's the purpose of const swap() function?
Asked Answered
C

3

32

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?

Cowl answered 7/4, 2022 at 13:49 Comment(1)
Since C++23, interesting.Kory
N
20

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.

Natoshanatron answered 7/4, 2022 at 14:2 Comment(2)
Great thanks! Looks like the const qualified swap is just a hack. To fix the root problem, we have to overhaul the copy/move assignment operators.Cowl
Is that because tmp would contain a reference to i?Caswell
S
5

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> is true for all i from 0 to sizeof...(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> &.

Scholarship answered 7/4, 2022 at 13:59 Comment(0)
S
4

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:

  1. The program is ill-formed if (std::is_swappable_v<const Types> && ...) is not true.

That is: You can only const swap the tuples if the types in the tuple can be const swapped.

Speaker answered 7/4, 2022 at 14:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.