2011-09-30 8 views
6

Tengo un problema en el que me gustaría proporcionar una versión genérica de una función foo que solo se puede aplicar cuando no existe absolutamente ninguna otra coincidencia para una invocación. ¿Cómo puedo modificar el siguiente código para que last_resort::foo coincidan peor con derived::type que con base::foo? Me gustaría encontrar una solución que no implique modificar la definición de bar y que preserve el tipo de argumento de last_resort::foo.¿Cómo hacer que una plantilla de función tenga la menor prioridad durante la ADL?

#include <iostream> 

namespace last_resort 
{ 

template<typename T> void foo(T) 
{ 
    std::cout << "last_resort::foo" << std::endl; 
} 

} 

template<typename T> void bar(T) 
{ 
    using last_resort::foo; 
    foo(T()); 
} 

namespace unrelated 
{ 

struct type {}; 

} 

namespace base 
{ 

struct type {}; 

void foo(type) 
{ 
    std::cout << "base::foo" << std::endl; 
} 

} 

namespace derived 
{ 

struct type : base::type {}; 

} 

int main() 
{ 
    bar(unrelated::type()); // calls last_resort::foo 
    bar(base::type());  // calls base::foo 
    bar(derived::type()); // should call base::foo, but calls last_resort::foo instead 

    return 0; 
} 

Respuesta

0

last_resort::foo se puede quitar de la sobrecarga de conjunto con disable_if. La idea es deshabilitar last_resort::foo(T) si foo(T) está bien formado. Esto hace que last_resort::foo(T) a ser el peor partido de foo:

namespace test 
{ 

template<typename T> struct has_foo { ... }; 

} 

namespace last_resort 
{ 

template<typename T> 
    struct disable_if_has_foo 
    : std::enable_if< 
     !test::has_foo<T>::value 
     > 
{}; 

template<typename T> 
    typename disable_if_has_foo<T>::type foo(T) 
{ 
    std::cout << "last_resort::foo" << std::endl; 
} 

} 

La salida:

$ g++ last_resort.cpp 
$ ./a.out 
last_resort::foo 
base::foo 
base::foo 

This answer describe cómo construir una solución para la comprobación de la existencia de una función (foo) volviendo void.

1

No se puede hacer mucho al respecto. Ambas funciones foo están en el conjunto de sobrecarga. Pero su last_resort one es una mejor coincidencia simplemente porque no requiere una conversión a diferencia de base :: foo para derived :: type(). Solo en el caso en que dos candidatos son "igualmente buenos" a juzgar por los parámetros y las posibles conversiones, se prefiere una plantilla no.

+0

Eso es cierto, pero me pregunto si hay una forma de introducir una conversión "oculta" en foo aumentándola con algunos parámetros adicionales con valores predeterminados. –

+0

No creo que funcione. Por lo que puedo decir, lo "mejor" que puede obtener es una ambigüedad para que la compilación falle. Pero tal vez, me falta algo, también. – sellibitze

+0

La solución parece ser decorar el tipo de retorno '' 'last_resort :: foo''' con algo como' '' disable_if_foo_exists :: type''', que usará SFINAE para comprobar si hay '' 'foo libre '' 'función. Si existe, '' 'last_resort :: foo''' se eliminaría del conjunto de sobrecarga. –

0

Puede proporcionar una sobrecarga de bar para el tipo derived::type después de la declaración de derived::type. Esto puede estar en namespace derived o no.

void bar(derived::type) 
{ 
    foo(derived::type()); 
} 
2

Esto sería casi tan malo como se pone:

struct badParam { template <typename T> badParam(T t) { } }; 
namespace last_resort { 
    void foo(badParam, int dummy = 0, ...) { 
    std::cout << "last_resort::foo" << std::endl; 
    } 
} 

Tienes una conversión definida por el usuario, un parámetro en default y una elipsis sin usar.

[editar]

variante leve, para salvar T moví la conversión definida por el usuario para el parámetro dummy:

struct badParam { 
    badParam() { } 
    operator int() { return 42; } 
}; 
namespace last_resort { 
    template <typename T> void foo(T t, int dummy = badParam(), ...) { 
    std::cout << "last_resort::foo" << std::endl; 
    } 
} 
+0

Gracias. Creo que agregar solo '' 'badParam''' es suficiente para degradarlo, pero borra el tipo de' '' T''' en la conversión. –

+0

Bueno, para un caso de último recurso, en general no se puede confiar en T, porque se obtienen todos los tipos extraños. Pero salvarlo es difícil. Deducción de argumento de la plantilla no produce esas conversiones definidas por el usuario. – MSalters

+0

Publicación antigua, pero pensamiento aleatorio: ¿qué hay de usar el 'badParam' original, pero haciendo' badParam' un tipo de plantilla con un puntero de valor de argumento de plantilla a la función real para invocar, que lo hace en su constructor. – Yakk

Cuestiones relacionadas