为rvalue专门化std::swap函数

7
在标准库中(20.2.2 [utility.swap]),std::swap被定义为左值引用。我理解这是交换两个物品时的普遍情况。然而,在某些情况下,交换rvalue是正确和可取的(例如当临时对象包含引用时,像这里:swap temporary tuples of references)。
为什么没有为rvalues重载呢?是否在rvalues上进行无意义交换的风险超过了潜在的好处?
是否有一种合法的方法来支持交换包含引用的rvalue std::tuple对象?对于用户定义类型,我会专门化swap以按值接受其参数,但似乎不适用于像std::tuple这样的库类型。

4
这不就是将rvalue分配给lvalue对象吗?因为之后无法再使用临时对象,所以最好直接进行(rvalue)赋值,而不是交换。 - Excelcius
3
对于"有时候交换rvalue是正确和可取的(比如当临时对象包含引用时)"这个说法,我需要一些解释。我认为你可能是错误的。像@Excelcius所说,交换rvalue似乎在语义上没有意义。 - Casey
@BenJones 为了将临时变量传递给一个需要进行交换的函数,它们必须是可引用的,这意味着你首先必须拥有一个左值。对于不可引用的两个项目进行交换是没有意义的。 - Zac Howland
4
包含引用的元组是无法进行“交换”操作的,因为引用不具备可交换性。 - Casey
1
@Yakk 但是为了使其有意义,这些参数中至少有1个必须是lvalue...否则它什么也不做。而且尝试交换引用会出错,因为你不能将引用重新分配给新位置(根据定义§17.6.3.2,它们是不可交换的)。 - Zac Howland
显示剩余19条评论
2个回答

3

要不我们创建一个lvalue_cast实用函数,将右值强制转换为左值:

#include <tuple>
#include <iostream>

template <class T>
T&
lvalue_cast(T&& t)
{
    return t;
}

int
main()
{
    int i = 1;
    int j = 2;
    swap(lvalue_cast(std::tie(i)), lvalue_cast(std::tie(j)));
    std::cout << i << '\n';
    std::cout << j << '\n';
}

因为交换引用 - 因此包含引用的元组仍然是未定义行为。 - Casey
@Casey: <耸肩> 有人忘了告诉委员会 ([tuple.assign]/p3) 这个事情。不将元组的复制赋值操作默认化的原因就在于处理 tuple<X&> 的情况。 - Howard Hinnant
1
lvalue_cast重命名为copy如何?这样更符合move的风格;) - fredoverflow
1
@HowardHinnant,您是在暗示编译器会对所有可能的未定义行为进行警告吗? - Casey
@HowardHinnant:这肯定违反了上面引用的“swap”语义。 - Lightness Races in Orbit
显示剩余6条评论

3

一些STL算法似乎需要对r-values进行交换,如果迭代器产生了一个r-value(例如临时值)。

是否应该让swap与r-values一起工作是一个有效的讨论。我经常看到这个问题,因为对于“特殊”的迭代器,*it会产生一个r-value,有时你想在“交换”时做正确的事情。

我找到的解决办法是首先专门为这些特殊的迭代器专门指定std::iter_swap。我认为这解决了所有STL算法的问题。

namespace std{
void iter_swap(special_iterator it1, special_iterator it2){
   ... special swap of pointed values
}
}

我认为这是所有排列STL算法都缺失和缺乏的一个基本定制点。
(当然,如果我们到了篡改STL的程度,我们也可以做namespace std {template<class T,...enable_if T&& is r-value ref只是以防万一...> void swap(T&& t1,T&& t2){swap(t1,t2);} 。但这更加危险。)

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