You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何std::swap不交换std::reference_wrapper底层对象?如何实现mysort?

实现多参数排序函数 mysort 的问题解答

问题背景

我尝试实现一个名为mysort的函数,它接受多个同类型参数并对其排序。例如以下代码应输出1 2 3:

template <typename ... Args>
void mysort(Args& ... args);
int main(int, char**)
{
    int x = 3;
    int y = 1;
    int z = 2;
    mysort(x, y, z);
    cout << x << ' ' << y << ' ' << z << endl;
    return 0;
}

尝试的方案

我提出的实现方案如下:

#include <array>
#include <algorithm>
#include <functional>

template <typename T, typename ... Args>
void mysort(T& t, Args& ... args)
{
    constexpr size_t n = sizeof...(Args) + 1;
    std::array<std::reference_wrapper<T>, n> temp = { std::ref(t), std::ref(args)... };
    std::sort(temp.begin(), temp.end());
}

但该方案无效,因为std::swap对std::reference_wrapper的操作不符合预期(我期望对std::reference_wrapper的修改能同步到其指向的底层对象)。

添加以下代码后功能恢复正常:

namespace std
{
    void swap(std::reference_wrapper<int>& a, std::reference_wrapper<int>& b) noexcept
    {
        std::swap(a.get(), b.get());
    }
}

但我不确定标准是否要求std::sort必须使用std::swap,因此该方案可能依赖具体实现,并不完整。

问题总结

我原本认为std::reference_wrapper应完全表现得像引用,但实际并非如此。我的问题如下:

  1. 实现mysort的最佳方式是什么?
  2. 为何标准不为std::reference_wrapper提供可交换底层对象的std::swap特化?

解答

1. 实现mysort的最佳方式

有几种更可靠的实现方式,避免依赖自定义的std::swap特化:

方式一:值拷贝排序后赋值回原变量

直接把参数值拷贝到数组,排序后再逐个赋值回原参数,简单且完全符合标准:

#include <array>
#include <algorithm>

template <typename T, typename... Args>
void mysort(T& first, Args&... rest)
{
    constexpr size_t count = 1 + sizeof...(Args);
    std::array<T, count> values = {first, rest...};
    std::sort(values.begin(), values.end());

    // 用折叠表达式将排序后的值赋值回原参数
    size_t idx = 0;
    ((first = values[idx++]), ..., (rest = values[idx++]));
}

注:该方式要求T可拷贝,这对支持排序的类型通常不是问题。

方式二:自定义交换逻辑的引用包装器方案

若要避免拷贝(比如针对大对象),可以给std::sort传递自定义交换函数,无需修改std命名空间:

#include <array>
#include <algorithm>
#include <functional>

template <typename T, typename... Args>
void mysort(T& first, Args&... rest)
{
    constexpr size_t count = 1 + sizeof...(Args);
    std::array<std::reference_wrapper<T>, count> refs = {std::ref(first), std::ref(rest)...};
    
    std::sort(refs.begin(), refs.end(),
        [](const auto& a, const auto& b) { return a.get() < b.get(); },
        [](auto& a, auto& b) { std::swap(a.get(), b.get()); });
}

C++17及以上的std::sort支持自定义交换操作,完全符合标准规范。

如果使用C++20范围库,代码更简洁:

#include <array>
#include <ranges>
#include <functional>

template <typename T, typename... Args>
void mysort(T& first, Args&... rest)
{
    std::array<std::reference_wrapper<T>, 1 + sizeof...(Args)> refs = {std::ref(first), std::ref(rest)...};
    std::ranges::sort(refs, {}, &std::reference_wrapper<T>::get);
}

通过投影&std::reference_wrapper<T>::get直接比较底层对象,std::ranges::sort会正确处理交换逻辑。

2. 为何标准不为std::reference_wrapper提供交换底层对象的std::swap特化

标准未提供该特化,是因为std::reference_wrapper的核心语义是可拷贝的引用容器,而非底层对象的代理。默认的std::swap行为是交换两个包装器的指向(让它们各自引用对方原来的对象),这才符合其"引用容器"的设计定位。

如果提供交换底层对象的特化,会与reference_wrapper的语义冲突:用户若想交换两个包装器的指向,将无法实现。标准库的设计原则是让组件行为清晰、符合语义定义,避免歧义。若需交换底层对象,显式调用std::swap(a.get(), b.get())才是正确做法。


内容的提问来源于stack exchange,提问作者arthurbesse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 23:42:33