ограничение использования шаблонов c++ типами POD

У меня есть класс шаблона c++, который работает правильно, только если шаблонный тип-это простые старые данные. Все, что с конструктором, который что-либо делает, будет работать неправильно.

Я хотел бы как-то получить предупреждение compiletime или runtime, когда кто-то пытается сделать это в любом случае.

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

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

есть ли трюк, чтобы сделать это?

5 ответов


#include <type_traits>

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

    // stuff here...
};

вышеуказанное вызовет ошибку компиляции, если вы передадите тип не-POD в качестве параметра шаблона. Для этого решения требуется C++11 для и static_assert ключевое слово.

EDIT: вы также можете реализовать это в C++03, Если ваш компилятор поддерживает TR1 (большинство):

#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...
};

Если у вас есть поддержка C++11 std::is_pod должен делать именно то, что вам нужно. Используйте его с std:: enable_if или с отправкой тегов. Например что-то вроде этого:

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
}

С static_assert вероятно, достаточно в большинстве случаев, используя enable_if и отправка тегов дает большую гибкость пользователям вашего класса способами SFINAE. Подумайте:

#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!
}

теперь, если вы удалите 2-й аргумент шаблона из myclass определение, и вместо этого, как и другие предложили, добавьте

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

внутри класса, вторая строка в main() просто не удастся скомпилировать, запустив static_assert.

об этом сказал ошибки static_assert гораздо более дружелюбны к человеческому наблюдателю, чем те из неудачников enable_if. Итак, если static_assert работает для вас, пойти на это. В противном случае, если вам нужно быть дружелюбнее к общему программированию вокруг вашего класса, подумайте о добавлении пояснительного комментария вокруг enable_if:

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

если все вокруг вас является C++11-свободно.

в реальной жизни, это хорошая идея, чтобы объяснить почему T должно быть POD оба для static_assert и за комментарий тексты.


если у вас нет C++11

если целевые типы POD ограничены (int, float, ...) Вы можете поместить реализацию в .cpp файл и явный экземпляр его для этих типов:

:

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

:

#include "myclass.h"

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

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

после этого, myclass просто можно использовать для этих типов и перерывов для других.


С type_traits, и static_assert, это довольно легко :

#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
}