我有一个c ++模板类,只有在模板化类型是普通旧数据时才能正常运行。任何带有构造函数的东西都无法正常工作。
无论如何,当有人试图这样做时,我想以某种方式得到编译时或运行时警告。
//this should generate error
myclass<std::string> a;
//this should be fine
myclass<int> b;
这样做有诀窍吗?
答案 0 :(得分:36)
#include <type_traits>
template<typename T>
class myclass
{
static_assert(std::is_pod<T>::value, "T must be POD");
// stuff here...
};
如果您将非POD类型作为模板参数传递,则上述操作将导致编译错误。此解决方案需要C ++ 11用于<type_traits>
标头和static_assert
关键字。
编辑:如果您的编译器支持TR1(大多数情况下),您也可以在C ++ 03中实现它:
#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...
};
答案 1 :(得分:10)
如果你有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
}
答案 2 :(得分:6)
虽然在大多数情况下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!
}
现在,如果从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流利的。
在现实生活中,为static_assert
和评论文本解释为什么 T必须是POD是一个好主意。
答案 3 :(得分:4)
如果您还没有C ++ 11
如果目标POD类型有限(int
,float
,...)您可以将实现放入.cpp
文件中并为这些类型显式实例化:
.h
档案:
template <typename T>
class myclass
{
T data;
public:
void func();
};
.cpp
档案:
#include "myclass.h"
template <typename T>
void myclass<T>::func()
{
}
template class myclass<float>;
template class myclass<int>;
template class myclass<char>;
...
之后,myclass
只适用于那些类型,而其他类型则可用。
答案 4 :(得分:0)
使用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
}
答案 5 :(得分:0)
将Simple's的答案更新为newer C++20 standard changes,std::is_pod<T>
将被弃用。由于这是Google在本主题中最明显的回应,因此,我将介绍其他人的不同之处,以期找到最新的答案。
POD type 作为普通旧数据的定义-等同于C结构。从C ++ 11到C ++ 20的POD要求是:
对于那些真的不知道用法之间有什么区别的人,这是经验法则。
std::is_trivial
。它保证了该对象的内存副本将创建精确的副本,不需要构造或解构。您可以分配内存并粘贴从套接字接收的内容。这是通过套接字传输数据或将其存储在通用缓冲区中时的基本用法。std::is_standard_layout
可以保证不同的C ++标准之间的兼容性,因为有关内存对齐的规则会随着时间而改变,并且某些实现可能会使用一种标准版本所保证的功能,而另一种标准版本则放宽了这些功能。差异与内存排序限制有关,例如每个下一个成员应具有更高的内存地址或第一个成员应具有整个结构的地址。