template sirven que plantillas para los funciones c++ design-patterns templates

c++ - sirven - Cómo provocar intencionalmente un error en tiempo de compilación en la creación de instancias de plantillas



plantillas en c++ (6)

A veces, cuando se codifica con plantillas C ++, se debe evitar que los usuarios creen una especialización específica o un conjunto de especializaciones, porque el resultado sería absurdo. De modo que puede definir una especialización (específica o parcial) cuya definición, si se crea una instancia, causaría un error de compilación. El objetivo sería, si un usuario "utiliza mal" la plantilla, causar un error de compilación justo al lado de un comentario en el archivo de encabezado explicando qué no hacer, en lugar de dejar que el compilador presente un mensaje de error confuso por sí mismo dispositivos, o tal vez permitir que compile el código cuestionable.

Ejemplo:

template <typename T> struct MyClassTemplate { // ... }; template <typename T> struct MyClassTemplate<T*> { // Do not use MyClassTemplate with a pointer type! typedef typename T::intentional_error err; };

Hay varias maneras de hacerlo (dependiendo de si su especialización es una especialización completa o parcial de una clase o función). Pero la sintaxis utilizada debe (?) Depender de un parámetro de plantilla, o de lo contrario el compilador se quejará cuando analice por primera vez la definición de error intencional. El ejemplo anterior tiene un agujero en el hecho de que alguien podría definir obstinadamente un tipo anidado intentional_error o un typedef de miembro (aunque diría que entonces se merecerían los problemas que surjan como resultado). Pero si utiliza un truco demasiado elegante, es probable que obtenga un mensaje de error del compilador indescifrable y / o engañoso, que en su mayoría infringe el propósito.

¿Hay mejores formas directas de no permitir instancias de plantillas?

Soy consciente de que en C ++ 0x, los conceptos de plantilla y las declaraciones de funciones eliminadas proporcionarán un control mucho mejor sobre este tipo de cosas, pero estoy buscando respuestas que sean válidas C ++ 03.


"¿Hay mejores formas directas de no permitir instancias de plantillas?" Nada significativamente mejor que lo que ya ha identificado. Estoy bastante seguro de que los mecanismos de protección de C ++ están ahí para protegerte de accidentes y no de malicia. Y alguien que defina una especialización o una clase para romper su uso previsto lo consideraría malicioso. Tal vez podrías golpear a la persona en la parte posterior de la cabeza cada vez que lo hagan.

Personalmente prefiero poner los cheques en plantillas que existen solo para describir los cheques. Eso permite combinaciones interesantes de herencia y plantillas.

template <class T> class not_with_pointer_t { }; template <class T> class not_with_pointer_t<T*>; template <class T> class some_class_t : public not_with_pointer_t<T> { }; template <class T, template <class U> class base_t> class another_class_t : public base_t<T> { }; typedef some_class_t<int> a_t; // ok typedef some_class_t<void*> b_t; // error if instantiated typedef another_class_t<void*, not_with_pointer_t> c_t; // error if instantiated template <class T> class unrestricted_t { }; typedef another_class_t<void*, unrestricted_t> d_t; // ok



Para mí, esto suena como un caso típico de static_assert de C ++ 0x o BOOST_STATIC_ASSERT . La funcionalidad static_assert tiene la ventaja de que puede pasar un mensaje de error personalizado para que el motivo del error sea más claro.

Ambas formas le dan la oportunidad de finalizar prematuramente el proceso de compilación en alguna condición de compilación definida a medida.

con static_assert:

template <typename T> struct MyClassTemplate<T*> { static_assert(always_false<T>::value, "Do not use MyClassTemplate with a pointer type!"); };

con BOOST_STATIC_ASSERT

template <typename T> struct MyClassTemplate<T*> { // Do not use MyClassTemplate with a pointer type! BOOST_STATIC_ASSERT(always_false<T>::value); };

Siempre falso se vería algo como esto:

template< typename T > struct always_false { enum { value = false }; };

HTH

Editar: corrigió los ejemplos para que realmente funcionaran ;-) ¡Gracias a GMan!


Podrías omitir definirlo.

template <typename T> struct MyClassTemplate<T*>;

También podría derivar de una especialización no definida

template <typename T> struct invalid; template <typename T> struct MyClassTemplate<T*> : invalid<T> { };

Tenga en cuenta que las especializaciones explícitas que declaran clases o funciones nunca dependerán de los parámetros de la plantilla. Por lo tanto, cosas como esta que dependen de los parámetros de la plantilla no pueden funcionar de todos modos. En ese caso, declarar una especialización explícita no definida debería ser suficiente

template<> struct MyClassTemplate<int*>;


Si no quiere usar una biblioteca, esta construcción es bastante confiable (es más o menos lo que Boost hace internamente):

template <typename T> void must_be_specialized(T const&) { enum dummy { d = (sizeof(struct must_be_specialized_for_this_type) == sizeof(T)) }; }

Puede poner algo análogo en una especialización para no permitir la creación de instancias de la plantilla con ese tipo. Personalmente, no me preocuparía que must_be_specialized_for_this_type obtuviera una definición de alguna parte, pero podría usar una declaración directa para arrancarla en un espacio de nombres privado si realmente quisiera.


boost::enable_if