想象一下,我正在写一些容器模板或其他东西。现在是时候专门研究std::swap
了。作为一个好公民,我会通过这样的方式启用ADL:
template <typename T>
void swap(my_template<T>& x, my_template<T>& y) {
using std::swap;
swap(x.something_that_is_a_T, y.something_that_is_a_T);
}
这非常整洁。直到我想添加一个异常规范。只要swap
的互换为noexcept
,我的T
就是noexcept
。所以,我会写一些类似的东西:
template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
noexcept(noexcept(swap(std::declval<T>(), std::declval<T>())))
问题是,swap
中的swap
需要是ADL发现的std::swap
或{{1}}。我该如何处理?
答案 0 :(得分:34)
我想我会把它移到一个单独的命名空间
namespace tricks {
using std::swap;
template <typename T, typename U>
void swap(T &t, U &u) noexcept(noexcept(swap(t, u)));
}
template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
noexcept(noexcept(tricks::swap(std::declval<T>(), std::declval<T>())))
{
using std::swap;
swap(x.something_that_is_a_T, y.something_that_is_a_T);
}
或者,您可以将整个代码移到tricks
并委托给那里。
答案 1 :(得分:10)
返回类型有similar problem:
// Want to be compatible with both boost::tuple and std::tuple
template<typename Tuple>
auto first(Tuple&& tuple)
-> /* ??? */
{
// Introduce name into scope
using std::get;
// but ADL can still pick boost::get for boost::tuple
return get<0>(std::forward<Tuple>(tuple));
}
使用decltype( get<0>(std::forward<Tuple>(tuple)) )
不正确,因为get
不在范围内。
可能的解决方法是:
在封闭范围内引入虚拟模板(在我的示例中为get
,在您的案例中为swap
);这包括将using std::swap
声明放在封闭的命名空间中,但存在污染命名空间的缺点。
使用类型特征:typename std::tuple_element<0, typename std::remove_reference<Tuple>::type>::type
(实际上这个是有问题的,但出于不属于此的原因)在我的示例中,并且在您的情况下可能是is_nothrow_swappable<T>::value
。然后,如果需要,专业化允许模板扩展到其他类型。
答案 2 :(得分:5)
我不会声明但不能定义一个似乎可能导致混淆的函数模板,而是编写自己的类型特征(无论如何,这应该是标准库中的特性)。在标准库的引导下,我将定义如下内容:
#include <type_traits>
#include <utility>
namespace adl {
using std::swap;
template<typename T, typename U>
struct is_nothrow_swappable : std::integral_constant<
bool,
noexcept(swap(std::declval<T &>(), std::declval<U &>()))
> {
};
} // namespace adl
我们必须定义我们自己的命名空间以将std :: swap导入(以避免将其提供给每个人),但当然,如果它在标准库中而不必要,因为它们已经可以进行不合格的调用交换。
答案 3 :(得分:1)
C ++ 17用std :: is_nothrow_swappable解决了这个特殊用例:http://en.cppreference.com/w/cpp/types/is_swappable