Hay dos búsquedas que se realizan para el nombre bar
. Una de ellas es la búsqueda sin reservas en el contexto de la definición foo
. El otro es dependiente de búsqueda de discusión en cada contexto de instancias (pero el resultado de la búsqueda en cada instanciación contexto no se le permite cambiar el comportamiento entre dos contextos de creación de instancias diferentes).
para obtener el comportamiento deseado, se podía ir y definir una función de respaldo en un espacio de nombres fallback
que devuelve algún tipo único
namespace fallback {
// sizeof > 1
struct flag { char c[2]; };
flag bar(...);
}
elSe llamará a la funciónsi nada más coincide porque la elipsis tiene el peor costo de conversión.Ahora, incluya los candidatos en su función mediante una directiva de uso de fallback
, de modo que fallback::bar
se incluya como candidato en la llamada al bar
.
Ahora, para ver si una llamada al bar
resuelve su función, la llamará y verificará si el tipo de devolución es flag
. El tipo de devolución de una función elegida de otro modo podría ser nulo, por lo que debe realizar algunos trucos de operador de coma para evitarlo.
namespace fallback {
int operator,(flag, flag);
// map everything else to void
template<typename T>
void operator,(flag, T const&);
// sizeof 1
char operator,(int, flag);
}
Si se ha seleccionado la función de nuestra entonces la invocación operador coma devolverá una referencia a int
. Si no es así o si la función seleccionada devolvió void
, la invocación devuelve void
por turno. A continuación, la siguiente invocación con flag
como segundo argumento devolverá un tipo que tenga sizeof 1 si se seleccionó nuestro respaldo, y un tamaño de mayor 1 (el operador de coma incorporado se usará porque void
está en la mezcla) si se seleccionó algo más .
Comparamos el tamaño de y lo delegamos en una estructura.
template<bool>
struct foo_impl;
/* bar available */
template<>
struct foo_impl<true> {
template<typename T>
static void foo(T const &t) {
bar(t);
}
};
/* bar not available */
template<>
struct foo_impl<false> {
template<typename T>
static void foo(T const&) {
std::cout << "not available, calling baz...";
}
};
template <typename T>
void foo(const T& t) {
using namespace fallback;
foo_impl<sizeof (fallback::flag(), bar(t), fallback::flag()) != 1>
::foo(t);
}
Esta solución es ambigua si la función existente también tiene puntos suspensivos. Pero eso parece ser bastante improbable. Prueba utilizando el repliegue:
struct C { };
int main() {
// => "not available, calling baz..."
foo(C());
}
Y si un candidato se encuentra el uso de las operaciones de búsqueda depende argumento
struct C { };
void bar(C) {
std::cout << "called!";
}
int main() {
// => "called!"
foo(C());
}
Para probar las operaciones de búsqueda no cualificada en el contexto definición, vamos a definir la siguiente función anterior foo_impl
y foo
(poner el foo_impl foo
plantilla anterior, por lo que tienen ambos el mismo contexto definición)
void bar(double d) {
std::cout << "bar(double) called!";
}
// ... foo template ...
int main() {
// => "bar(double) called!"
foo(12);
}
Buena pregunta. +1 –