C ++ función plantilla especialización parcial?

Sé que el siguiente código es una especialización parcial de una clase:

template  class MyClass { … }; // partial specialization: both template parameters have same type template  class MyClass { … }; 

También sé que C ++ no permite la especialización parcial de la plantilla de función (solo está permitido). Pero, ¿significa mi código que he especializado parcialmente mi plantilla de función para argumentos del mismo tipo? Porque funciona para Microsoft Visual Studio 2010 Express! Si no, ¿podría explicar el concepto de especialización parcial?

 #include  using std::cin; using std::cout; using std::endl; template  inline T1 max (T1 const& a, T2 const& b) { return a < b ? b : a; } template  inline T const& max (T const& a, T const& b) { return 10; } int main () { cout << max(4,4.2) << endl;; cout << max(5,5) <>z; } 

En el ejemplo, en realidad está sobrecargando (no especializando) la función max . La syntax de especialización parcial debería haber parecido más o menos a continuación ( si se hubiera permitido ):

 //Partial specialization is not allowed by the spec, though! template  inline T const& max (T const& a, T const& b) { ^^^^^ < --- specializing here return 10; } 

[Nota: en el caso de una plantilla de función, el estándar de C ++ solo permite la especialización completa (excluidas las extensiones del comstackdor).]

Como la especialización parcial no está permitida, como apuntan otras respuestas, podría std::is_same utilizando std::is_same y std::enable_if , como se muestra a continuación:

 template  inline typename std::enable_if::value, void>::type typed_foo(const F& f) { std::cout < < ">>> messing with ints! " < < f << std::endl; } template  inline typename std::enable_if::value, void>::type typed_foo(const F& f) { std::cout < < ">>> messing with floats! " < < f << std::endl; } int main(int argc, char *argv[]) { typed_foo("works"); typed_foo(2); } 

Salida:

 $ ./a.out >>> messing with ints! works >>> messing with floats! 2 

Editar : en caso de que necesite tratar todos los demás casos restantes, puede agregar una definición que indique que los casos tratados no deberían coincidir ; de lo contrario, caería en definiciones ambiguas. La definición podría ser:

 template  inline typename std::enable_if< (not std::is_same::value) and (not std::is_same::value), void>::type typed_foo(const F& f) { std::cout < < ">>> messing with unknown stuff! " < < f << std::endl; } int main(int argc, char *argv[]) { typed_foo("works"); typed_foo(2); typed_foo("either"); } 

Que produce:

 $ ./a.out >>> messing with ints! works >>> messing with floats! 2 >>> messing with unknown stuff! either 

Aunque esta cuestión de todos los casos parece un poco aburrida, ya que tienes que decirle al comstackdor todo lo que ya has hecho, es bastante factible tratar hasta 5 o más especializaciones.

¿Qué es especialización?

Si realmente quieres entender las plantillas, debes echar un vistazo a los lenguajes funcionales. El mundo de las plantillas en C ++ es una sublengua puramente funcional propia.

En los lenguajes funcionales, las selecciones se realizan mediante la coincidencia de patrones :

 -- An instance of Maybe is either nothing (None) or something (Just a) -- where a is any type data Maybe a = None | Just a -- declare function isJust, which takes a Maybe -- and checks whether it's None or Just isJust :: Maybe a -> Bool -- definition: two cases (_ is a wildcard) isJust None = False isJust Just _ = True 

Como puede ver, sobrecargamos la definición de isJust .

Bueno, las plantillas de clase C ++ funcionan exactamente de la misma manera. Proporciona una statement principal que indica el número y la naturaleza de los parámetros. Puede ser solo una statement, o también actúa como una definición (su elección), y luego puede (si así lo desea) proporcionar especializaciones del patrón y asociarlas a una versión diferente (de lo contrario sería tonto) de la clase .

Para las funciones de plantilla, la especialización es algo más incómoda: entra en conflicto con la resolución de sobrecarga. Como tal, se ha decidido que una especialización se relacionaría con una versión no especializada, y no se considerarían las especializaciones durante la resolución de sobrecarga. Por lo tanto, el algoritmo para seleccionar la función correcta se convierte en:

  1. Realice la resolución de sobrecarga, entre las funciones regulares y plantillas no especializadas
  2. Si se selecciona una plantilla no especializada, compruebe si existe una especialización que sería una mejor coincidencia

(para un tratamiento en profundidad, ver GotW # 49 )

Como tal, la especialización de plantillas de funciones es un ciudadano de la segunda zona (literalmente). En lo que a mí respecta, estaríamos mejor sin ellos: aún no me he encontrado con un caso en el que el uso de una especialización de plantilla no se pudiera resolver con la sobrecarga en su lugar.

¿Es esto una especialización de plantilla?

No, es simplemente una sobrecarga, y esto está bien. De hecho, las sobrecargas suelen funcionar como esperamos, mientras que las especializaciones pueden ser sorprendentes (recuerde el artículo de GotW que he vinculado).

No. Por ejemplo, legalmente puede especializar std::swap , pero no puede definir legalmente su propia sobrecarga. Eso significa que no puede hacer que std::swap funcione para su propia plantilla de clase personalizada.

La sobrecarga y la especialización parcial pueden tener el mismo efecto en algunos casos, pero lejos de todo.

La especialización parcial no clasificada y no variable no está permitida, pero como se dijo:

Todos los problemas en la informática se pueden resolver con otro nivel de indirección. – David Wheeler

Agregar una clase para reenviar la llamada a la función puede resolver esto, aquí hay un ejemplo:

 template  struct enable_fun_partial_spec; struct fun_tag {}; template  constexpr R fun(Ts&&... ts) { return enable_fun_partial_spec::call( std::forward(ts)...); } template  struct enable_fun_partial_spec { constexpr static R call(Ts&&... ts) { return {0}; } }; template  struct enable_fun_partial_spec { constexpr static R call(T, T) { return {1}; } }; template  struct enable_fun_partial_spec { constexpr static R call(int, int) { return {2}; } }; template  struct enable_fun_partial_spec { constexpr static R call(int, char) { return {3}; } }; template  struct enable_fun_partial_spec { constexpr static R call(char, T2) { return {4}; } }; static_assert(std::is_same_v(1, 1)), int>, ""); static_assert(fun(1, 1) == 2, ""); static_assert(std::is_same_v(1, 1)), char>, ""); static_assert(fun(1, 1) == 2, ""); static_assert(std::is_same_v(1L, 1L)), long>, ""); static_assert(fun(1L, 1L) == 1, ""); static_assert(std::is_same_v(1L, 1L)), double>, ""); static_assert(fun(1L, 1L) == 1, ""); static_assert(std::is_same_v(1u, 1)), int>, ""); static_assert(fun(1u, 1) == 0, ""); static_assert(std::is_same_v(1, 'c')), char>, ""); static_assert(fun(1, 'c') == 3, ""); static_assert(std::is_same_v('c', 1)), unsigned>, ""); static_assert(fun('c', 1) == 4, ""); static_assert(std::is_same_v(10.0, 1)), unsigned>, ""); static_assert(fun(10.0, 1) == 0, ""); static_assert( std::is_same_v(1, 2, 3, 'a', "bbb")), double>, ""); static_assert(fun(1, 2, 3, 'a', "bbb") == 0, ""); static_assert(std::is_same_v()), unsigned>, ""); static_assert(fun() == 0, ""); 

Respuesta tardía, pero algunos lectores tardíos pueden encontrarla útil: a veces, una función auxiliar, diseñada de tal manera que puede ser especializada, también puede resolver el problema.

Entonces imaginemos, esto es lo que tratamos de resolver:

 template  void function(X x, Y y) { R* r = new R(x); f(r, y); // another template function? } // for some reason, we NEED the specialization: template  void function(int x, Y y) { // unfortunately, Wrapper has no constructor accepting int: Wrapper* w = new Wrapper(); w->setValue(x); f(w, y); } 

OK, especialización de función de plantilla parcial, no podemos hacer eso … Así que “exportaremos” la parte necesaria para la especialización a una función auxiliar, especialícela y úsela:

 template  R* create(T t) { return new R(t); } template <> Wrapper* create(int n) // fully specialized now -> legal... { Wrapper* w = new Wrapper(); w->setValue(n); return w; } template  void function(X x, Y y) { R* r = create(x); f(r, y); // another template function? } 

Esto puede ser interesante especialmente si las alternativas (sobrecargas normales en lugar de especializaciones, la solución propuesta por Rubens, … – no es que estas sean malas o la mía sea mejor, solo otra ) compartirían bastante código común.