restringir el uso de plantillas de c++ a tipos de POD


Tengo una clase de plantilla c++, que solo funciona correctamente si el tipo templatizado es datos antiguos. Cualquier cosa con un constructor que haga cualquier cosa no funcionará correctamente.

Me gustaría de alguna manera obtener una advertencia de tiempo de compilación o tiempo de ejecución cuando alguien intente hacerlo de todos modos.

//this should generate error
myclass<std::string> a;

//this should be fine
myclass<int> b;

¿Hay algún truco para hacer esto?

Author: Paula_plus_plus, 2013-10-03

5 answers

#include <type_traits>

template<typename T>
class myclass
{
    static_assert(std::is_pod<T>::value, "T must be POD");

    // stuff here...
};

Lo anterior causará un error de compilación si pasa un tipo no POD como parámetro de plantilla. Esta solución requiere C++11 para el encabezado <type_traits> y la palabra clave static_assert.

EDITAR: También puede implementar esto en C++03 si su compilador admite TR1 (la mayoría lo hace):

#include <tr1/type_traits>

template<typename T>
class myclass
{
    static char T_must_be_pod[std::tr1::is_pod<T>::value ? 1 : -1];

    // stuff here...
};
 34
Author: Simple,
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
2013-10-03 08:24:03

Si tiene soporte para C++11, std::is_pod debería hacer exactamente lo que necesita. Utilícelo con std:: enable_if o con tag dispatch. Por ejemplo algo como esto:

template <typename T, typename Enable = void>
class Test;

template<typename T>
class Test<T, typename std::enable_if<std::is_pod<T>::value, void>::type>
{};

int main() {
    Test<int> t1;
    //Test<std::string> t2; <-this will not compile
}
 10
Author: magor,
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
2013-10-03 08:43:30

Mientras que el static_assert probablemente sea suficiente en la mayoría de los casos, usar enable_if y tag dispatch da mayor flexibilidad a los usuarios de tu clase por los caminos de SFINAE. Considere:

#include <type_traits>
#include <string>
#include <iostream>
template <typename T,
    class=typename std::enable_if< std::is_pod<T>::value >::type>
struct myclass
{
    typedef T value_type;
    T data;
};

template <typename T>
void enjoy(T)
{
    std::cout << "Enjoying T!" << std::endl;
}

template <typename T>
void enjoy(typename myclass<T>::value_type)
{
    std::cout << "Enjoying myclass<T>::value_type!" << std::endl;
}

int main()
{
    enjoy<int>(int()); // prints: Enjoying myclass<T>::value_type!
    enjoy<std::string>(std::string()); // SFINAE at work - prints: enjoying T!
    myclass<int> i; // compiles OK
    //myclass<std::string> s; // won't compile - explicit instantiation w/non-POD!
}

Ahora si elimina el argumento de la segunda plantilla de la definición myclass, y en su lugar, como otros han sugerido, agregue un

  static_assert(std::is_pod<T>::value, "POD expected for T");

Dentro de la clase, la segunda línea en main() simplemente fallará al compilar, activando el static_assert.

Dicho esto, los errores de static_assert son mucho más amigables para observador humano, que los de los fallidos enable_if. Entonces, si static_assert funciona para ti, ve por ello. De lo contrario, si necesita ser más amigable con la programación genérica alrededor de su clase, considere agregar un comentario explicativo alrededor de enable_if:

 // POD expected for T
 class=typename std::enable_if< std::is_pod<T>::value >::type>

A menos que todo el mundo a su alrededor sea fluido en C++11.

En la vida real, es una buena idea explicar por qué T debe ser POD tanto para static_assert como para los textos de comentarios.

 5
Author: Vassilii Khachaturov,
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
2013-10-03 09:53:08

Si no tiene C++11

Si los tipos de POD objetivo son limitados (int, float, ...) Puede poner la implementación en un archivo .cpp y crear instancias explícitas para esos tipos:

.h archivo:

template <typename T>
class myclass
{
    T data;
public:
    void func();
};

.cpp archivo:

#include "myclass.h"

template <typename T>
void myclass<T>::func()
{
}

template class myclass<float>;
template class myclass<int>;
template class myclass<char>;
...

Después de eso, myclass es solo utilizable para esos tipos y se rompe para otros.

 4
Author: deepmax,
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
2013-10-03 08:19:44

Con type_traits, y static_assert, es bastante fácil:

#include <type_traits>

struct A{
};
struct B{
    virtual ~B(){}
};

template< class T >
struct MyClass
{
    static_assert( std::is_pod<T>::value, "not a POD" );
};

int main()
{
    MyClass<A> a;
    //MyClass<B> b; -- break, cause not a POD
}
 0
Author: BЈовић,
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
2013-10-03 08:20:40