Imagine I\'m writing some container template or something. And the time comes to specialize std::swap
for it. As a good citizen, I\'ll enable ADL by doing somet
I think I would move it into a separate namespace
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);
}
Alternatively you can move the whole code up into tricks
and delegate to there.
C++17 has solved this particular use case with std::is_nothrow_swappable: http://en.cppreference.com/w/cpp/types/is_swappable
Rather than declaring but not defining a function template, which seems likely to cause confusion, I would write my own type trait (which is what should probably be in the standard library, anyway). Following the lead of the standard library, I would define something like the following:
#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
We have to define our own namespace to import std::swap into (to avoid giving it to everyone), but of course, if it were in the standard library that wouldn't be necessary because they can already make unqualified calls to swap.
There is a similar problem for return types:
// 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));
}
Using decltype( get<0>(std::forward<Tuple>(tuple)) )
isn't correct as get
isn't in scope.
Possible workarounds are:
Introducing a dummy template (get
in my example, swap
in your case) in the enclosing scope; this includes putting the using std::swap
declaration in the enclosing namespace, with the drawback of polluting the namespace.
Use of a type trait: typename std::tuple_element<0, typename std::remove_reference<Tuple>::type>::type
(actually this one is problematic but for reasons that don't belong here) in my example, and a potential is_nothrow_swappable<T>::value
in your case. Specializations then allow the template to be extended for other types if need be.