¿Por qué la plantilla de funciones no se puede especializar parcialmente?

87

Sé que la especificación del lenguaje prohíbe la especialización parcial de la plantilla de función.

Me gustaría saber la razón por la que lo prohíbe. ¿No son útiles?

template<typename T, typename U> void f() {}   //allowed!
template<> void f<int, char>()            {}   //allowed!
template<typename T> void f<char, T>()    {}   //not allowed!
template<typename T> void f<T, int>()     {}   //not allowed!
Nawaz
fuente
Porque template<typename T, typename U> void f(T t, U u) {}también template<> void f(int t, char u) {}está permitido.
dashesy
10
Me parece interesante que la gente siga proporcionando soluciones cuando la pregunta no es "cómo puedo lograr un objetivo similar", sino "cuál es la razón fundamental de este comportamiento" ... Yo mismo no sé el motivo de esta elección, pero asumo que El comité debe haber tenido una razón para prohibir la especialización parcial de la plantilla de función. Hasta ahora, la explicación "más cercana" es el enlace publicado por Georgy, que solo señala los "riesgos" potenciales de la especialización parcial de la plantilla de función cuando hay sobrecargas. Sin embargo, no creo que esa sea una razón para prohibir esta función, así que supongo que hay más en esto ..
bartgol

Respuestas:

59

AFAIK que ha cambiado en C ++ 0x.

Supongo que fue solo un descuido (considerando que siempre se puede obtener el efecto de especialización parcial con un código más detallado, colocando la función como staticmiembro de una clase).

Puede buscar el DR (Informe de defectos) correspondiente, si lo hay.

EDITAR : revisando esto, encuentro que otros también han creído eso, pero nadie puede encontrar tal apoyo en el borrador del estándar. Este hilo SO parece indicar que la especialización parcial de las plantillas de funciones no es compatible con C ++ 0x .

EDICIÓN 2 : solo un ejemplo de lo que quise decir con "colocar la función como staticmiembro de una clase":

#include <iostream>
using namespace std;

// template<typename T, typename U> void f() {}   //allowed!
// template<> void f<int, char>()            {}   //allowed!
// template<typename T> void f<char, T>()    {}   //not allowed!
// template<typename T> void f<T, int>()     {}   //not allowed!

void say( char const s[] ) { std::cout << s << std::endl; }

namespace detail {
    template< class T, class U >
    struct F {
        static void impl() { say( "1. primary template" ); }
    };

    template<>
    struct F<int, char> {
        static void impl() { say( "2. <int, char> explicit specialization" ); }
    };

    template< class T >
    struct F< char, T > {
        static void impl() { say( "3. <char, T> partial specialization" ); }
    };

    template< class T >
    struct F< T, int > {
        static void impl() { say( "4. <T, int> partial specialization" ); }
    };
}  // namespace detail

template< class T, class U >
void f() { detail::F<T, U>::impl(); }    

int main() {
    f<char const*, double>();       // 1
    f<int, char>();                 // 2
    f<char, double>();              // 3
    f<double, int>();               // 4
}
Saludos y hth. - Alf
fuente
¿tienes el estándar en n3225? Hice una búsqueda rápida pero no pude encontrarla: /
Matthieu M.
1
ah lo siento ... faltaba una palabra. Tengo el documento, pero no pude encontrar el párrafo en particular . Aunque dada su edición, supongo que es simplemente porque no está allí :)
Matthieu M.
3
Esto no se cambia en C ++ 0x. También dudo de su utilidad. Siempre puede sobrecargar la plantilla y hacer uso del pedido parcial .
Johannes Schaub - litb
1
Actualización tardía: no cambió incluso en C ++ 17 ocho años después, y tampoco parece ingresar a C ++ 20. Sin embargo, no veo ninguna razón para ...
Aconcagua
Creo que esta es, con mucho, la implementación más completa del concepto
Víctor
18

Bueno, realmente no puede hacer una especialización parcial de funciones / métodos, sin embargo, puede hacer una sobrecarga.

template <typename T, typename U>
T fun(U pObj){...}

// acts like partial specialization <T, int> AFAIK 
// (based on Modern C++ Design by Alexandrescu)
template <typename T>
T fun(int pObj){...} 

Es el camino pero no sé si te satisface.

Michal W
fuente
1
Wow, mi mente estaba llena de plantillas de modo que realmente olvidé lo simples que podían ser las cosas :)
Johannes
1
Desafortunadamente, no es el caso cuando desea pasar argumentos variados después de especializar parcialmente la función .. :(
Gwangmu Lee
No estoy seguro de qué significa pasar plantillas variadas, así que me gustaría saber en qué se diferencia de una especialización parcial. ¿Podría proporcionar más detalles?
Beginpluses
¿Qué sucede si solo desea dos funciones para todos los tipos integrales y flotantes?
Dmitriy Dokshin
14

En general, no se recomienda especializar las plantillas de funciones en absoluto, debido a problemas de sobrecarga. Aquí hay un buen artículo del C / C ++ Users Journal: http://www.gotw.ca/publications/mill17.htm

Y contiene una respuesta honesta a su pregunta:

Por un lado, no puedes especializarlos parcialmente, básicamente porque el idioma dice que no puedes.

Georgy Pashkov
fuente
3
El artículo no trata sobre la especialización parcial, además de que se menciona una vez.
Euri Pinhollow
11

Dado que puede especializar clases parcialmente, puede usar un functor:

#include <iostream>

template < typename dtype , int k > struct fun
{
 int operator()()
 {
  return k ;
 }
} ;

template < typename dtype > struct fun < dtype , 0 >
{
 int operator()()
 {
  return 42 ;
 }
} ;

int main ( int argc , char * argv[] )
{
 std::cout << fun<float,5>()() << std::endl ;
 std::cout << fun<float,0>()() << std::endl ;
}
Kay F. Jahnke
fuente
1
A continuación, puede utilizar una plantilla de función única para realizar las llamadas, deshaciéndose de la ()()sintaxis fea .
tmr232