假设我想要适用于rvalues的swap
,并且不想为rvalue/lvalue引用的所有组合编写4个版本(rvalue/rvalue版本有点没有意义,但不会造成伤害)。我想出了这个:
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);
}
这似乎按预期工作。这个可以吗?还是我错过了重要的事情而使我以后难受?
最佳答案
虽然我认为您的实现没有固有的概念缺陷,但我将提出三个建议。
(注意:我在这里将实现将右值交换为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<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
>
在不从模板推论中排除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());
它可以编译,即使它显然不应该编译! A
和B
甚至不相关,只要碰巧相互引用就可以构造。重用代码[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
。关于c++ - 与右值交换,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18942438/