【问题标题】:Swapping with rvalues与右值交换
【发布时间】:2013-09-27 08:34:02
【问题描述】:

假设我想要在右值上工作的swap,并且不想为右值/左值引用的所有组合编写 4 个版本(右值/右值版本有点毫无意义,但它没有伤害)。我想出了这个:

template <typename A, typename B>
struct is_same_no_ref
    : std::is_same<
        typename std::remove_reference<A>::type,
        typename std::remove_reference<B>::type
    >
{};

template <typename A, typename B,
    typename = typename std::enable_if<is_same_no_ref<A, B>::value>::type
>
inline void my_swap(A&& a, B&& b) {
    typename std::remove_reference<A>::type t = std::move(a);
    a = std::move(b);
    b = std::move(t);
}

这似乎按预期工作。这个可以吗?还是我错过了一些重要的东西,会让我以后受苦?

【问题讨论】:

  • 为什么不直接:template &lt;typename A, typename B&gt; void swap(A&amp;&amp; a, B&amp;&amp; b) { auto t = move(a); a = move(b); b = move(t); }
  • @MM。这是不太对称的。我确实意识到它只有在 AB 无论如何都“相互移动”时才会起作用,但仍然如此。
  • 我相信你的意思是is_same_no_ref而不是std::is_same_no_ref

标签: c++ swap move-semantics rvalue-reference


【解决方案1】:

虽然我认为您的实现没有固有的概念缺陷,但我会提出三个建议。

注意:这里我将右值交换这个概念的实现称为swap_rvalues


从模板推导中排除 const 类型

  • 如何?

假设

template <typename A, typename B>
struct is_same_no_ref
    : std::is_same<
        typename std::remove_reference<A>::type,
        typename std::remove_reference<B>::type
    >
{};

启用条件std::enable_if&lt;std::is_same_no_ref&lt;A, B&gt;::value&gt;更改为以下内容。

std::enable_if<
    std::is_same_no_ref<A, B>::value &&
    !std::is_const<typename std::remove_reference<A>::type>::value &&
    !std::is_const<typename std::remove_reference<B>::type>::value
>
  • 为什么?

模板推导不排除const类型,将const变量传递给swap_rvalues,如下,

int const a = 0, b = 0;
swap_rvalues(a, b);

诱使编译器标记有关内部实现细节的错误,这对用户不太友好。


启用条件移至返回类型声明

  • 如何?

代替

template<typename A, typename B, typename = typename std::enable_if<...>::type>
inline void swap_rvalues(A&& a, B&& b);

像下面这样声明它

template<typename A, typename B>
inline typename std::enable_if<...>::type swap_rvalues(A&& a, B&& b);
  • 为什么?

尽管高度不太可能,但swap_rvalues 的第三个模板参数的显式定义是可能的,有效地覆盖了启用条件。这可能允许编译不应该编译的代码,并且可能会出现讨厌的情况。使用启用条件的返回类型声明可以完全避免这种情况。

考虑以下示例。

template <
        typename A, 
        typename B, 
        typename = typename std::enable_if<
            is_same_no_ref<A, B>::value &&
            !std::is_const<typename std::remove_reference<A>::type>::value &&
            !std::is_const<typename std::remove_reference<B>::type>::value
        >::type>
inline void
swap(A&& a, B&& b) {
    typename std::remove_reference<A>::type t = std::move(a);
    a = std::move(b);
    b = std::move(t);
}

struct B;
struct A{A(){} A(B const&){}};
struct B{B(){} B(A const&){}};
swap<A, B, void>(A(), B());

它编译,即使它显然不应该! AB 甚至没有关系,它们只是碰巧在给定另一个引用的情况下是可构造的。


重用代码 [aka KISS]

  • 如何?

由于右值已经命名,只需转发调用std::swap,而不是提供swap_rvalues 的全新实现。

  • 为什么?

为什么是reinventing the wheel†? std::swap 已经在为右值命名后提供了预期的行为,那么为什么不重用它呢?


结论

swap_rvalues‡ 的最终实现如下所示。

template <typename A, typename B>
inline typename std::enable_if<
    is_same_no_ref<A, B>::value &&
    !std::is_const<typename std::remove_reference<A>::type>::value &&
    !std::is_const<typename std::remove_reference<B>::type>::value
>::type
swap_rvalues(A&& a, B&& b) {
    std::swap(a, b);
}

脚注

†​​ 重新发明轮子就是复制一个已经被其他人创建或优化过的基本方法。”

swap_rvalues 实际上在实际场景中最好称为swap

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-24
    • 1970-01-01
    • 2014-07-25
    • 2012-06-30
    相关资源
    最近更新 更多