2009-10-27 20 views
22

tengo el siguiente código que compila y funciona bien:C++ especialización de plantilla sin función por defecto

template<typename T> 
T GetGlobal(const char *name); 

template<> 
int GetGlobal<int>(const char *name); 

template<> 
double GetGlobal<double>(const char *name); 

Sin embargo Quiero eliminar la función "por defecto". Es decir, deseo realizar todas las llamadas a GetGlobal <t> donde 't' no es un error int o double double.

Por ejemplo, GetGlobal <char>() debería ser un error de tiempo de compilación.

Intenté simplemente eliminar la función predeterminada, pero, como imaginaba, recibí muchos errores ... ¿Hay alguna manera de "desactivarlo" y permitir llamadas solo a las versiones especializadas de la función?

Gracias!

Respuesta

22

Para llegar a implementar un error en tiempo de compilación como:

template<typename T> 
T GetGlobal(const char *name) { T::unimplemented_function; } 
// `unimplemented_function` identifier should be undefined 

Si usa Boost que podría hacerlo más elegante:

template<typename T> 
T GetGlobal(const char *name) { BOOST_STATIC_ASSERT(sizeof(T) == 0); } 

garantías C++ estándar que no existe tal tipo que tiene un tamaño igual a 0, por lo que obtendrá un error en tiempo de compilación.

Como sbi sugirió en sus comentarios la última podría reducirse a:

template<typename T> 
T GetGlobal(const char *name) { char X[!sizeof(T)]; } 

Yo prefiero la primera solución, ya que da mensaje de error más claro (al menos en Visual C++) que los otros.

+4

Para que el primer caso de fallar en _compile real_, Koper necesitaría una función _undeclared_, no una _undefined_. Y la única forma de hacer que compile esto es hacerlo dependiente de 'T'. Algo como 'T :: some_thing_that_is_definitely_undeclared' podría hacer. (El segundo podría ser imitado con 'char dummy [! Sizeof (T)];'.) – sbi

+1

El uso de 'unimplemented_function' da un mensaje de error más claro (al menos en Visual C++) que usando' T :: unimplemented_function'. –

+1

@Kirill: El uso de 'unimplemented_function' no funcionará en un compilador que realice una búsqueda en dos fases. (Actualmente, VC no hace esto). Tal compilador no compilará la plantilla _definition_, aunque nunca se haya instanciado. – sbi

3

Sugeriría no proporcionar una implementación, solo una simple declaración del método.

La otra opción sería usar una afirmación en tiempo de compilación. Boost tiene una serie de tales bestias.

namespace mpl = boost::mpl; 
BOOST_MPL_ASSERT((mpl::or_< boost::same_type<T, double>, 
          boost::same_type<T, int> >)); 

También existe su contraparte de la versión del mensaje, que ayudaría.

+0

+1. las afirmaciones de mpl son muy agradables :) –

5

Si no lo implementa, al menos obtendrá un error de enlazador. Si quieres un error en tiempo de compilación, se puede hacer esto con plantillas de clase:

template<typename T> 
struct GlobalGetter; 

template<> 
struct GlobalGetter<int> { 
    static int GetGlobal(const char *name); 
}; 

template<> 
struct GlobalGetter<double> { 
    static double GetGlobal(const char *name); 
}; 

template<typename T> 
T GetGlobal(const char *name) 
{ 
    return GlobalGetter<T>::GetGlobal(name); 
} 
+0

Cualquier idea _por qué_ una declaración de función forward de plantilla no produce un error de compilación, mientras que una declaración de clase de plantilla forward lo hace? – xtofl

+0

Demasiadas escrituras si tiene muchas más especializaciones que para 'int' y 'doble'. –

+2

¿Qué es 'GlobalGetter (name)'? ¿Constructor? No hay ningún constructor con argumento único en 'GlobalGetter ' y 'GlobalGetter '. –

1

Los siguientes son técnicas alternativas a la utilización de impulso:

declarar un typedef a un nombre dependiente

Esto funciona porque la búsqueda de nombre para DONT solo ocurre cuando 'T' ha sido reemplazado. Esta es una versión similar (pero legal) del ejemplo dado por Kirill

template <typename T> 
T GetGlobal (const char * name) { 
    typedef typename T::DONT CALL_THIS_FUNCTION; 
} 

uso de un tipo de retorno incompleta

Esta técnica no funciona para especializaciones, pero que trabajará para sobrecargas.La idea es que su legal para declarar una función que devuelve un tipo incompleto, pero no llamarlo:

template <typename T> 
class DONT_CALL_THIS_FUNCTION GetGlobal (const char * name); 
7

pesar de que es un viejo y anticuado cuestión, se puede destacar que C++11 habían resuelto este problema mediante borrado funciones:

template<typename T> 
T GetGlobal(const char *name) = delete; 

template<> 
int GetGlobal<int>(const char *name); 

ACTUALIZACIÓN

Esto no va a compilar bajo MacOS llvm 8. Se debe a un defecto pendiente de 4 años (ver this bug report).

La siguiente solución resolverá el problema (utilizando una construcción static_assert).

template<typename T> 
T GetGlobal(const char *name) { 
    static_assert(sizeof(T) == 0, "Only specializations of GetGlobal can be used"); 
} 

template<> 
int GetGlobal<int>(const char *name); 
+0

Esto es lo mejor, ¡gracias! También podría ser relevante aquí: https://stackoverflow.com/questions/12629191/compile-time-error- para-no-instanciado-plantilla-miembros-en vez-de-tiempo-link-er – egpbos

Cuestiones relacionadas