¿Especialización parcial de la plantilla de función C++?


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

template <typename T1, typename T2> 
class MyClass { 
  … 
}; 


// partial specialization: both template parameters have same type 
template <typename T> 
class MyClass<T,T> { 
  … 
}; 

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

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

template <typename T1, typename T2> 
inline T1 max (T1 const& a, T2 const& b) 
{ 
    return a < b ? b : a; 
} 

template <typename T> 
inline T const& max (T const& a, T const& b)
{
    return 10;
}


int main ()
{
    cout << max(4,4.2) << endl;;
    cout << max(5,5) << endl;
    int z;
    cin>>z;
}
Author: Narek, 2011-11-09

6 answers

En el ejemplo, en realidad estás sobrecargando (no especializándote) la función max<T1,T2>. Especialización parcial sintaxis debería haberse visto algo como abajo ( si se hubiera permitido):

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

[Nota: en el caso de una plantilla de función, solo completo la especialización está permitida por el estándar C++ (excluyendo las extensiones del compilador).]

 60
Author: iammilind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-21 14:06:17

Dado que la especialización parcial no está permitida as como apuntaban otras respuestas --, podría trabajar alrededor de ella usando std::is_same y std::enable_if, como se muestra a continuación:

template <typename T, class F>
inline typename std::enable_if<std::is_same<T, int>::value, void>::type
typed_foo(const F& f) {
    std::cout << ">>> messing with ints! " << f << std::endl;
}

template <typename T, class F>
inline typename std::enable_if<std::is_same<T, float>::value, void>::type
typed_foo(const F& f) {
    std::cout << ">>> messing with floats! " << f << std::endl;
}

int main(int argc, char *argv[]) {
    typed_foo<int>("works");
    typed_foo<float>(2);
}

Salida:

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

Editar: En caso de que necesite ser capaz de tratar todos los otros casos restantes, podría agregar una definición que indique que los casos ya tratados no deberían coincidir con otherwise de lo contrario caería en definiciones ambiguas. La definición podría ser:

template <typename T, class F>
inline typename std::enable_if<(not std::is_same<T, int>::value)
    and (not std::is_same<T, float>::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<int>("works");
    typed_foo<float>(2);
    typed_foo<std::string>("either");
}

Que produce:

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

Aunque esto todos los casos parece un poco aburrido, ya que tienes que decirle al compilador todo lo que ya has hecho, es bastante factible tratar hasta 5 o algunas especializaciones más.

 32
Author: Rubens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-19 15:15:56

¿Qué es la especialización?

Si realmente quieres entender las plantillas, deberías echar un vistazo a los lenguajes funcionales. El mundo de las plantillas en C++ es un sublenguaje puramente funcional propio.

En los lenguajes funcionales, las selecciones se realizan usando 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 puedes ver, nosotros sobrecargamos la definición de isJust.

Bueno, las plantillas de clase C++ funcionan exactamente de la misma manera. Usted proporciona un principal declaración, que indica el número y la naturaleza de los parámetros. Puede ser solo una declaración, 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 asociarles una versión diferente (de lo contrario sería una tontería) 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 un la versión no especializada y las especializaciones no se considerarían durante la resolución de sobrecarga. Por lo tanto, el algoritmo para seleccionar la función correcta se convierte en:

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

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

Como tal, plantilla la especialización de funciones es un ciudadano de segunda zona (literalmente). En lo que a mí respecta, estaríamos mejor sin ellos: todavía tengo que encontrar un caso en el que un uso de especialización de plantillas no se pueda resolver con sobrecarga.

¿Es esto una especialización de plantilla ?

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

 12
Author: Matthieu M.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-09 07:49:12

No. Por ejemplo, puede especializarse legalmente 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 todos.

 4
Author: Puppy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-09 07:09:19

No se permite la especialización parcial no-clase, no-variable, pero como se dijo:

Todos los problemas en la computadora la ciencia puede ser resuelta por 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 <class Tag, class R, class... Ts>
struct enable_fun_partial_spec;

struct fun_tag {};

template <class R, class... Ts>
constexpr R fun(Ts&&... ts) {
  return enable_fun_partial_spec<fun_tag, R, Ts...>::call(
      std::forward<Ts>(ts)...);
}

template <class R, class... Ts>
struct enable_fun_partial_spec<fun_tag, R, Ts...> {
  constexpr static R call(Ts&&... ts) { return {0}; }
};

template <class R, class T>
struct enable_fun_partial_spec<fun_tag, R, T, T> {
  constexpr static R call(T, T) { return {1}; }
};

template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, int> {
  constexpr static R call(int, int) { return {2}; }
};

template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, char> {
  constexpr static R call(int, char) { return {3}; }
};

template <class R, class T2>
struct enable_fun_partial_spec<fun_tag, R, char, T2> {
  constexpr static R call(char, T2) { return {4}; }
};

static_assert(std::is_same_v<decltype(fun<int>(1, 1)), int>, "");
static_assert(fun<int>(1, 1) == 2, "");

static_assert(std::is_same_v<decltype(fun<char>(1, 1)), char>, "");
static_assert(fun<char>(1, 1) == 2, "");

static_assert(std::is_same_v<decltype(fun<long>(1L, 1L)), long>, "");
static_assert(fun<long>(1L, 1L) == 1, "");

static_assert(std::is_same_v<decltype(fun<double>(1L, 1L)), double>, "");
static_assert(fun<double>(1L, 1L) == 1, "");

static_assert(std::is_same_v<decltype(fun<int>(1u, 1)), int>, "");
static_assert(fun<int>(1u, 1) == 0, "");

static_assert(std::is_same_v<decltype(fun<char>(1, 'c')), char>, "");
static_assert(fun<char>(1, 'c') == 3, "");

static_assert(std::is_same_v<decltype(fun<unsigned>('c', 1)), unsigned>, "");
static_assert(fun<unsigned>('c', 1) == 4, "");

static_assert(std::is_same_v<decltype(fun<unsigned>(10.0, 1)), unsigned>, "");
static_assert(fun<unsigned>(10.0, 1) == 0, "");

static_assert(
    std::is_same_v<decltype(fun<double>(1, 2, 3, 'a', "bbb")), double>, "");
static_assert(fun<double>(1, 2, 3, 'a', "bbb") == 0, "");

static_assert(std::is_same_v<decltype(fun<unsigned>()), unsigned>, "");
static_assert(fun<unsigned>() == 0, "");
 2
Author: user2709407,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-12 02:48:50

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

Así que imaginemos, esto es lo que intentamos resolver:

template <typename R, typename X, typename Y>
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 <typename R, typename Y>
void function<R, int, Y>(int x, Y y) 
{
    // unfortunately, Wrapper has no constructor accepting int:
    Wrapper* w = new Wrapper();
    w->setValue(x);
    f(w, y);
}

OK, especialización parcial de funciones de plantilla, no podemos hacer eso... Así que vamos a "exportar" la parte necesaria para la especialización en una función auxiliar, especialicarla y usarla:

template <typename R, typename T>
R* create(T t)
{
    return new R(t);
}
template <>
Wrapper* create<Wrapper, int>(int n) // fully specialized now -> legal...
{
    Wrapper* w = new Wrapper();
    w->setValue(n);
    return w;
}

template <typename R, typename X, typename Y>
void function(X x, Y y)
{
    R* r = create<R>(x);
    f(r, y); // another template function?
}

Esto puede ser interesante especialmente si el alternativas (sobrecargas normales en lugar de especializaciones, la solución propuesta por Rubens, ... - no es que estos sean malos o el mío es mejor, solo otro compartiría bastante código común.

 0
Author: Aconcagua,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-22 08:23:16