与右值交换

2024-05-03

假设我想要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);
}

这似乎按预期工作。这个可以吗?或者我错过了一些重要的事情,这会让我以后受苦?


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

(note:我将在这里提到这个概念的实现交换右值 as swap_rvalues)


从模板推导中排除 const 类型

  • How?

Assuming

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
    >
{};

改变启用条件 from std::enable_if<std::is_same_no_ref<A, B>::value>进入以下内容。

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
>
  • Why?

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

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

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


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

  • How?

代替

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);
  • Why?

虽然highly不太可能,第三个模板参数的显式定义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());

它可以编译,尽管它显然不应该编译!A and B甚至不相关,它们只是碰巧在给定另一个引用的情况下是可构造的。


重用代码[又名KISS http://en.wikipedia.org/wiki/KISS_principle]

  • How?

由于右值已经是给了一个名字, 只需转发呼叫std::swap http://en.cppreference.com/w/cpp/algorithm/swap,而不是提供一个全新的实现swap_rvalues.

  • Why?

Why 重新发明轮子 http://en.wikipedia.org/wiki/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);
}

脚注

"To reinvent the wheel is to duplicate a basic method that has already previously been created or optimized by others."

swap_rvalues in fact would better be called swap in a real scenario.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

与右值交换 的相关文章

随机推荐