c++ - que - ¿Cómo escribo un tipo de retorno final habilitado para ADL, o especificación noexcept?
funciones y procedimientos en python (4)
Imagina que estoy escribiendo una plantilla de contenedor o algo así. Y llega el momento de especializar std::swap
for it. Como buen ciudadano, habilitaré ADL haciendo algo como esto:
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);
}
Esto es muy limpio y todo. Hasta que yo quiera agregar una especificación de excepción. Mi swap
es noexcept
mientras que el swap para T
sea noexcept
. Entonces, estaría escribiendo algo como:
template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
noexcept(noexcept(swap(std::declval<T>(), std::declval<T>())))
El problema es que el swap
en el que se necesita ser el swap
descubierto por ADL o std::swap
. ¿Cómo manejo esto?
C ++ 17 ha resuelto este caso de uso particular con std :: is_nothrow_swappable: http://en.cppreference.com/w/cpp/types/is_swappable
Creo que lo movería a un espacio de nombres separado
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);
}
Alternativamente, puede mover todo el código hacia arriba en tricks
y delegar allí.
En lugar de declarar pero no definir una plantilla de función, que probablemente cause confusión, escribiría mi propio rasgo de tipo (que es lo que probablemente debería estar en la biblioteca estándar). Siguiendo el ejemplo de la biblioteca estándar, definiría algo como lo siguiente:
#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
Tenemos que definir nuestro propio espacio de nombres para importar std :: swap (para evitar que se lo entreguen a todos), pero, por supuesto, si fuera en la biblioteca estándar no sería necesario porque ya pueden hacer llamadas de intercambio no calificadas.
Hay un problema similar para los tipos de devolución:
// 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));
}
El uso de decltype( get<0>(std::forward<Tuple>(tuple)) )
no es correcto ya que get
no está dentro del alcance.
Las soluciones posibles son:
Presentando una plantilla ficticia (
get
en mi ejemplo,swap
en su caso) en el ámbito de aplicación adjunto; esto incluye colocar la declaraciónusing std::swap
en el espacio de nombres adjunto, con el inconveniente de contaminar el espacio de nombres.Uso de un rasgo de tipo:
typename std::tuple_element<0, typename std::remove_reference<Tuple>::type>::type
(en realidad este es problemático pero por razones que no pertenecen aquí) en mi ejemplo, y un valor potencialis_nothrow_swappable<T>::value
en su caso. Las especializaciones luego permiten que la plantilla se extienda a otros tipos si es necesario.