问题
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?
回答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.
回答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 (
getin my example,swapin your case) in the enclosing scope; this includes putting theusing std::swapdeclaration 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>::valuein your case. Specializations then allow the template to be extended for other types if need be.
回答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.
回答4:
C++17 has solved this particular use case with std::is_nothrow_swappable: http://en.cppreference.com/w/cpp/types/is_swappable
来源:https://stackoverflow.com/questions/7635939/how-do-i-write-an-adl-enabled-trailing-return-type-or-noexcept-specification