¿Cuál es el propósito de la función const swap()?

3 minutos de lectura

Al implementar una costumbre tuple (aquí)encontré que hay un cableado swap() función que toma const parámetros (preferencia cp):

template< class... Types >
constexpr void swap( const std::tuple<Types...>& lhs,
                     const std::tuple<Types...>& rhs ) noexcept(/* see below */);

y un const-calificado swap() función miembro (preferencia cp):

constexpr void swap( const tuple& other ) noexcept(/* see below */) const;

const significa que el objeto es de solo lectura, pero para intercambiar dos objetos, tiene que modificar los objetos, lo que viola el const-ness

Entonces, ¿cuál es el propósito de la función const swap()?

  • Desde C++23, interesante.

    –Kevin

    7 abr a las 13:53

avatar de usuario de eerorika
erorika

Esto se introdujo en la propuesta “zip” P2321 descrito originalmente en “Un plan para rangos de C++ 23” P2214.

P2321

  • intercambio por const tupla y const par. Una vez que las tuplas de referencias se hacen constantemente asignables, se puede llamar al std::swap predeterminado para las tuplas constantes de referencias. Sin embargo, ese intercambio de triple movimiento hace lo incorrecto:

    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
    

    Por lo tanto, este documento propone agregar sobrecargas de intercambio para tuplas y pares constantes para realizar correctamente el intercambio de elementos.

P2214 explica por qué se necesita la asignabilidad constante para la implementación de zip. Se deriva de que los operadores de asignación no están calificados como ref.

  • ¡Muchas gracias! Parece que el intercambio calificado const es solo un truco. Para solucionar el problema de raíz, tenemos que revisar los operadores de asignación de copiar/mover.

    – 方圆圆

    7 abr a las 17:03

  • ¿Es porque tmp contendría una referencia a i?

    – usuario253751

    8 abr a las 17:00

Te has perdido la nota al pie sobre cuándo está disponible esa sobrecarga:

Esta sobrecarga participa en la resolución de sobrecarga solo si std::is_swappable_v<const Ti> es true para todo i de 0 a sizeof...(Types).

Si tienes un tipo const_swappable tal que swap(const const_swappable &, const const_swappable &) es sensato, entonces no hay ninguna razón por la que no pueda intercambiar const std::tuple<const_swappable> &.

Como ejemplo, considere un tipo similar a un puntero, que puede intercambiar los valores del pointee sin modificar el puntero:

#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";

}

Y nota de preferencia cp:

  1. El programa está mal formado si (std::is_swappable_v<const Types> && ...) no es true.

Es decir: solo puede intercambiar constantemente las tuplas si los tipos en la tupla se pueden intercambiar constantemente.

¿Ha sido útil esta solución?