const swap()函数的目的是什么?

32

在实现一个自定义的tuple时(这里),我发现了一个奇怪的swap()函数,它传入了const参数(cppreference):

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

还有一个带有常量限定符的swap()成员函数(参见Cppreference):

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

const 表示对象是只读的,但是要交换两个对象,必须修改对象,这违反了常量性。

那么,const swap() 函数的目的是什么?


5
自C++23起,有趣。 - Kevin
3个回答

20

这是在“zip”提案中引入的 P2321,最初在“A Plan for C++23 Ranges”中描述 P2214

P2321

  • 对于const tuple和const pair进行交换。一旦将引用元组设为可进行const分配,就可以为const引用元组调用默认的std::swap。然而,那个三重移动交换做了错误的事情:

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

因此,本文提议为常量元组和对添加swap的重载,以正确执行逐个元素的交换。

P2214解释了为什么实现zip需要const可赋值性。这源于赋值运算符不是ref qualified。


非常感谢!看起来带有const限定符的swap只是一种hack。为了解决根本问题,我们必须重构复制/移动赋值运算符。 - El Mismo Sol
这是因为tmp会包含对i的引用吗? - user253751

5
您错过了有关该重载可用性的脚注:

只有当对于所有i从0到sizeof...(Types)std::is_swappable_v<const Ti>都为true时,此重载才参与重载决议。

如果您有一个类型const_swappable,��得swap(const const_swappable &, const const_swappable &)是明智的,则没有理由不能交换const std::tuple<const_swappable> &


4
例如,考虑一种类似指针的类型,它可以在不修改指针的情况下交换指向的值:
#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";

}

来自cppreference的注意事项:

  1. 如果(std::is_swappable_v<const Types> && ...)不是true,程序就是有问题的。

也就是说,只有在元组中的类型可以被常量交换时,才能进行常量互换。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接