实现扩展内省交换算法
Implementing extended introspective swap algorithm
我了解 ADL 和交换习语:
using std::swap;
swap(x, y);
boost::swap()
为您完成以上操作。现在,我想进一步推动它。具体来说,如果可能,让交换执行 x.swap(y)
,否则回退到 boost::swap()
。因此,您不必同时实施成员互换和免费互换,后者既冗长又多余。我试图实现这样的交换并最终得到以下结果。实现这样的事情会变得非常棘手。所以,我想知道我的实现是否有任何缺陷,或者是否存在更简洁的实现。
#include <algorithm>
#include <utility>
namespace cppu_detail_swap {
template <typename T>
void swap_impl(T& x, T& y) {
using std::swap;
swap(x, y);
}
} // namespace cppu_detail_swap
namespace cppu {
namespace detail {
template <typename T>
void swap(T& x, T& y, int) {
cppu_detail_swap::swap_impl(x, y);
}
template <typename T>
auto swap(T& x, T& y, char) -> decltype(x.swap(y)) {
return x.swap(y);
}
} // namespace detail
template <typename T>
void swap(T& x, T& y) {
detail::swap(x, y, ' ');
}
} // namespace cppu
您当前的解决方案对于 cppu 命名空间中的对象存在缺陷,例如
// [insert your code here]
namespace cppu
{
struct X{};
struct Y{ void swap(Y& y) { }; };
}
int main()
{
auto x1 = cppu::X{};
auto x2 = cppu::X{};
swap(x1, x2);
auto y1 = cppu::Y{};
auto y2 = cppu::Y{};
swap(y1, y2);
}
g++ 告诉我:
taste.cpp:9:7: error: call of overloaded ‘swap(cppu::X&, cppu::X&)’ is ambiguous
要摆脱它,您需要在 swap_impl
中显式调用 std::swap
,这没关系,因为您已经通过 cppu::swap 实现到达这里。但是你不会对其他类型使用重载。因此,我认为你需要区分三种情况:
- 有自己的swap成员函数
- 没有交换成员函数并且来自命名空间 cppu
- 没有swap成员函数,是任意其他命名空间(这里需要使用ADL swap惯用语)。
此外,我同意@Yakk 的观点,我会更直接而不是使用 int/char hack。
所以让我们开始吧:
检查交换成员可用性的助手:
namespace cppu
{
namespace detail
{
template <typename T>
using void_t = void;
template <typename T, typename = void>
struct has_member_swap
{
static constexpr bool value = false;
};
template <typename T>
struct has_member_swap<
T,
void_t<decltype(std::declval<T&>().swap(std::declval<T&>()))>>
{
static constexpr bool value = true;
};
}
}
以及检查 T
是否来自命名空间 cppu
的助手,另请参阅 :
namespace helper
{
template <typename T, typename = void>
struct is_member_of_cppu : std::false_type
{
};
template <typename T>
struct is_member_of_cppu<
T,
decltype(adl_is_member_of_cppu(std::declval<T>()))> : std::true_type
{
};
}
namespace cppu
{
template <typename T>
auto adl_is_member_of_cppu(T && ) -> void;
}
现在我们可以编写所有三个重载:
namespace cppu
{
namespace detail
{
template <
typename T,
typename = std::enable_if_t<helper::is_member_of_cppu<T>::value and
not has_member_swap<T>::value>>
auto swap(T& x, T& y)
-> std::enable_if_t<helper::is_member_of_cppu<T>::value and
not has_member_swap<T>::value>
{
std::cout << "cppu-type without member swap";
std::swap(x, y);
}
template <
typename T,
typename = std::enable_if_t<not helper::is_member_of_cppu<T>::value and
not has_member_swap<T>::value>>
auto swap(T& x, T& y)
-> std::enable_if_t<not helper::is_member_of_cppu<T>::value and
not has_member_swap<T>::value>
{
std::cout << "not cppu-type without member swap";
using std::swap;
swap(x, y);
}
template <typename T, typename = std::enable_if_t<has_member_swap<T>::value>>
auto swap(T& x, T& y) -> decltype(x.swap(y))
{
std::cout << "member swap";
return x.swap(y);
}
}
}
像以前一样调用它:
namespace cppu
{
template <typename T>
void swap(T& x, T& y)
{
detail::swap(x, y);
}
}
最后:测试整个事情。
namespace cppu
{
struct X{};
struct Y{ void swap(Y& y) { }; };
}
struct A{};
struct B{ void swap(B& y) { }; };
struct C{};
auto swap(C&, C&) -> void { std::cout << " with own overload"; }
static_assert(helper::is_member_of_cppu<cppu::X>::value, "");
static_assert(helper::is_member_of_cppu<cppu::Y>::value, "");
static_assert(not helper::is_member_of_cppu<A>::value, "");
static_assert(not helper::is_member_of_cppu<B>::value, "");
int main()
{
auto x1 = cppu::X{};
auto x2 = cppu::X{};
std::cout << "X: "; swap(x1, x2); std::cout << std::endl;
auto y1 = cppu::Y{};
auto y2 = cppu::Y{};
std::cout << "Y: "; swap(y1, y2); std::cout << std::endl;
auto a1 = A{};
auto a2 = A{};
std::cout << "A: "; cppu::swap(a1, a2); std::cout << std::endl;
auto b1 = B{};
auto b2 = B{};
std::cout << "B: "; cppu::swap(b1, b2); std::cout << std::endl;
auto c1 = C{};
auto c2 = C{};
std::cout << "C: "; cppu::swap(c1, c2); std::cout << std::endl;
}
输出符合预期(恕我直言):
X: cppu-type without member swap
Y: member swap
A: not cppu-type without member swap
B: member swap
C: not cppu-type without member swap with own overload
我了解 ADL 和交换习语:
using std::swap;
swap(x, y);
boost::swap()
为您完成以上操作。现在,我想进一步推动它。具体来说,如果可能,让交换执行 x.swap(y)
,否则回退到 boost::swap()
。因此,您不必同时实施成员互换和免费互换,后者既冗长又多余。我试图实现这样的交换并最终得到以下结果。实现这样的事情会变得非常棘手。所以,我想知道我的实现是否有任何缺陷,或者是否存在更简洁的实现。
#include <algorithm>
#include <utility>
namespace cppu_detail_swap {
template <typename T>
void swap_impl(T& x, T& y) {
using std::swap;
swap(x, y);
}
} // namespace cppu_detail_swap
namespace cppu {
namespace detail {
template <typename T>
void swap(T& x, T& y, int) {
cppu_detail_swap::swap_impl(x, y);
}
template <typename T>
auto swap(T& x, T& y, char) -> decltype(x.swap(y)) {
return x.swap(y);
}
} // namespace detail
template <typename T>
void swap(T& x, T& y) {
detail::swap(x, y, ' ');
}
} // namespace cppu
您当前的解决方案对于 cppu 命名空间中的对象存在缺陷,例如
// [insert your code here]
namespace cppu
{
struct X{};
struct Y{ void swap(Y& y) { }; };
}
int main()
{
auto x1 = cppu::X{};
auto x2 = cppu::X{};
swap(x1, x2);
auto y1 = cppu::Y{};
auto y2 = cppu::Y{};
swap(y1, y2);
}
g++ 告诉我:
taste.cpp:9:7: error: call of overloaded ‘swap(cppu::X&, cppu::X&)’ is ambiguous
要摆脱它,您需要在 swap_impl
中显式调用 std::swap
,这没关系,因为您已经通过 cppu::swap 实现到达这里。但是你不会对其他类型使用重载。因此,我认为你需要区分三种情况:
- 有自己的swap成员函数
- 没有交换成员函数并且来自命名空间 cppu
- 没有swap成员函数,是任意其他命名空间(这里需要使用ADL swap惯用语)。
此外,我同意@Yakk 的观点,我会更直接而不是使用 int/char hack。
所以让我们开始吧:
检查交换成员可用性的助手:
namespace cppu
{
namespace detail
{
template <typename T>
using void_t = void;
template <typename T, typename = void>
struct has_member_swap
{
static constexpr bool value = false;
};
template <typename T>
struct has_member_swap<
T,
void_t<decltype(std::declval<T&>().swap(std::declval<T&>()))>>
{
static constexpr bool value = true;
};
}
}
以及检查 T
是否来自命名空间 cppu
的助手,另请参阅
namespace helper
{
template <typename T, typename = void>
struct is_member_of_cppu : std::false_type
{
};
template <typename T>
struct is_member_of_cppu<
T,
decltype(adl_is_member_of_cppu(std::declval<T>()))> : std::true_type
{
};
}
namespace cppu
{
template <typename T>
auto adl_is_member_of_cppu(T && ) -> void;
}
现在我们可以编写所有三个重载:
namespace cppu
{
namespace detail
{
template <
typename T,
typename = std::enable_if_t<helper::is_member_of_cppu<T>::value and
not has_member_swap<T>::value>>
auto swap(T& x, T& y)
-> std::enable_if_t<helper::is_member_of_cppu<T>::value and
not has_member_swap<T>::value>
{
std::cout << "cppu-type without member swap";
std::swap(x, y);
}
template <
typename T,
typename = std::enable_if_t<not helper::is_member_of_cppu<T>::value and
not has_member_swap<T>::value>>
auto swap(T& x, T& y)
-> std::enable_if_t<not helper::is_member_of_cppu<T>::value and
not has_member_swap<T>::value>
{
std::cout << "not cppu-type without member swap";
using std::swap;
swap(x, y);
}
template <typename T, typename = std::enable_if_t<has_member_swap<T>::value>>
auto swap(T& x, T& y) -> decltype(x.swap(y))
{
std::cout << "member swap";
return x.swap(y);
}
}
}
像以前一样调用它:
namespace cppu
{
template <typename T>
void swap(T& x, T& y)
{
detail::swap(x, y);
}
}
最后:测试整个事情。
namespace cppu
{
struct X{};
struct Y{ void swap(Y& y) { }; };
}
struct A{};
struct B{ void swap(B& y) { }; };
struct C{};
auto swap(C&, C&) -> void { std::cout << " with own overload"; }
static_assert(helper::is_member_of_cppu<cppu::X>::value, "");
static_assert(helper::is_member_of_cppu<cppu::Y>::value, "");
static_assert(not helper::is_member_of_cppu<A>::value, "");
static_assert(not helper::is_member_of_cppu<B>::value, "");
int main()
{
auto x1 = cppu::X{};
auto x2 = cppu::X{};
std::cout << "X: "; swap(x1, x2); std::cout << std::endl;
auto y1 = cppu::Y{};
auto y2 = cppu::Y{};
std::cout << "Y: "; swap(y1, y2); std::cout << std::endl;
auto a1 = A{};
auto a2 = A{};
std::cout << "A: "; cppu::swap(a1, a2); std::cout << std::endl;
auto b1 = B{};
auto b2 = B{};
std::cout << "B: "; cppu::swap(b1, b2); std::cout << std::endl;
auto c1 = C{};
auto c2 = C{};
std::cout << "C: "; cppu::swap(c1, c2); std::cout << std::endl;
}
输出符合预期(恕我直言):
X: cppu-type without member swap Y: member swap A: not cppu-type without member swap B: member swap C: not cppu-type without member swap with own overload