Sobrecarga de la plantilla de último recurso / catch-all / fallback

Como es evidente en una pregunta que hice anteriormente, Resolución de sobrecarga, plantillas y herencia , se elegirá una sobrecarga de plantilla antes de una sobrecarga que requiera conversión derivada a base.

Sin embargo, ¿existe una manera de proporcionar una sobrecarga alternativa que solo se seleccione como último recurso absoluto si no hay nada más que coincida? En este caso particular, se puede usar enable_if pero desafortunadamente no sería extensible.

Me gusta esto:

 // My library has this and has no knowledge of the possible overloads of foo template void foo(const T &) { /* Do something */ } // The user of the library provides this: void foo(const UserBaseType &) { /* Do something */ } // User calls foo with object derived from UserBaseType: foo(UserDerivedType()); 

En este caso, quiero que se llame a la sobrecarga UserBaseType, no a la sobrecarga de la plantilla.

Si está dispuesto a exigir a sus usuarios que proporcionen sus puntos de personalización a través de la búsqueda dependiente de argumentos (ADL) , puede lograr esto con la proverbial capa adicional de indirección. Primero, es posible determinar si la ADL para un nombre dado es exitosa al proporcionar el peor repliegue posible y determinar si la búsqueda del nombre lo selecciona [*]:

 namespace detail { // Simple trait that computes the inverse of std::is_same template  struct is_different : std::true_type {}; template  struct is_different : std::false_type {}; // The ellipsis conversion is worse than any other // conversion, so overload resolution will choose // this declaration of foo only if there is no // result from ADL. struct tag; tag foo(...); // Trait that determines if ADL for foo(T) succeeds. template  using has_adl_foo = is_different()))>; } 

Dado que la conversión de puntos suspensivos es estrictamente peor que una secuencia de conversión estándar o definida por el usuario según [over.ics.rank] / 2, cualquier personalización razonable de foo proporcionada por el usuario de la biblioteca será una mejor coincidencia.

Luego, necesita cierta maquinaria para distribuir entre su implementación alternativa y una personalización proporcionada por el usuario en base al rasgo has_adl_foo :

 namespace detail { // Fallback, used only if ADL fails. template  typename std::enable_if::value>::type impl(T&&) { std::cout << "Fallback\n"; } // Dispatch to foo found by ADL. template  typename std::enable_if::value, decltype(foo(std::declval())) >::type impl(T&& t) { return foo(std::forward(t)); } } template  auto foo(T&& t) -> decltype(detail::impl(std::forward(t))) { return detail::impl(std::forward(t)); } 

Luego, los usuarios pueden proporcionar sus personalizaciones de manera bastante simple, de todos modos, en comparación con las plantillas especializadas en el espacio de nombres de su biblioteca, declarando sobrecargas de foo en el espacio de nombres de sus declaraciones de clase donde ADL puede encontrarlas ( DEMO ):

 struct UserType {}; struct DerivedUserType : UserType {}; void foo(const UserType&) { std::cout << "User extension\n"; } 

[*]: Técnica de detección de ADL adaptada de la respuesta de @ TC a ¿Cuál es la forma correcta de implementar is_swappable para probar el concepto Swappable? .

El único parámetro que se garantiza que tiene una prioridad más baja que cualquier otra cosa son los variadics de estilo C: ... , y ciertamente no es lo que querría (o incluso podría) usar.

Me temo que no hay nada que proporcionar donde la única personalización del lado del usuario sería proporcionar una sobrecarga. Sin embargo, si puede tolerar una carga un poco mayor para el usuario, podría hacer que funcione con una clase de rasgo:

 template  struct HasCustomFoo : std::false_type {}; template ::value>::type> void foo(const T &) { /* Do something generic */} 

Luego, el usuario de la biblioteca debe especializar HasCustomFoo para todas las clases aplicables:

 template <> struct HasCustomFoo : std::true_type {}; template <> struct HasCustomFoo : std::true_type {}; void foo(const UserBaseType &) { /* Do something user-specific */ } foo(UserDerivedType()); // This now calls the user-specific function 

No es completamente automático, pero al menos la solución está en manos del usuario y la biblioteca puede seguir siendo genérica.