How do I write an ADL-enabled trailing return type, or noexcept specification?
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 something like this:
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);
}
This is very neat and all. Until I want to add an exception specification. My swap
is noexcept
as long as the swap for T
is noexcept
. So, I'd be writing something like:
template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
noexcept(noexcept(swap(std::declval<T>(), std::declval<T>())))
Problem is, the swap
in there needs to be the ADL-discovered swap
or std::swap
. How do I handle this?
Solution 1:
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.
Solution 2:
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 theusing 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 potentialis_nothrow_swappable<T>::value
in your case. Specializations then allow the template to be extended for other types if need be.
Solution 3:
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.
Solution 4:
C++17 has solved this particular use case with std::is_nothrow_swappable: http://en.cppreference.com/w/cpp/types/is_swappable