ограничение использования шаблонов 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
}