上午Randy同学碰到了一个array中应用tr1::is_pod导致的编译错误,看了一下,顺便了解了一些概念:
在开始之前,还是说说什么是tr1。
TR1,全称是“C++ Technical Report 1”,实际上是C++标准扩展库ISO/IEC TR 19768的一个俗称,是进入C++标准库的一个提案。TR1中包括的内容主要有:正则表达式(regular expressions)、智能指针(smart pointers),哈希表(hash tables)以及随机数产生器(random number generators),还包括函数对象(Function Objects),元编程和类型属性(Metaprogramming and Type Traits)等等很多内容。TR1本身并不是C++标准,而是一个草案。但是绝大多数情况下提案的内容都会成为下一个官方标准。
参见:
http://en.wikipedia.org/wiki/Technical_Report_1
下面说说我们使用is_pod相关的东西。
POD: 全称是“Plain Old Data”,是C++ 98标准(ISO/IEC 14882, first edition, 1998-09-01)中引入的一个概念,主要目的是兼容C和C++语言中对数据的比较。实质上POD类型主要包括int, char, float等原始类型及其集合类型(aggregate class)。
参见:
http://en.wikipedia.org/wiki/Plain_Old_Data_Structures
http://www.fnal.gov/docs/working-groups/fpcltf/Pkg/ISOcxx/doc/POD.html
POD Aggregate Class: 上面刚提到,就是一个只包含POD数据的类,不使用C++中的封装和面向对象的特性。其中还包含的意思是说,POD的定义是可以迭代的,如果一个类只包含POD数据,那这个类就属于POD。以此类推,如果一个类只包含POD数据或者POD类,那它也属于POD。
最后在说到is_pod之前,说说Randy同学碰到的编译错误:
error C2139: an undefined class is not allowed as an argument to compiler intrinsic type trait '__has_trivial_constructor'
Trivial Constructor:我们经常可以有看到trivial之类的词,到底是什么意思呢?如果去查词典,估计很难理解。个人的理解,就是轻量级的,类似于“浅拷贝”中的“浅”,或许是更浅的浅。Trivial Constructor其实是说更本不允许用户自己定义constructor,即使自己定义的constructor是一个空函数都不行。当然,定义虚函数也不行,因为虚函数会导致编译器自动生成的constructor中包含额外的操作。(这里trivial constructor也是个人不太明确的问题,本意应该是说更本没有constructor,但是MSDN的定义是:__has_trivial_constructor(type): Returns true if the type has a trivial, compiler-generated constructor。因此个人觉得可以理解成,没有constructor,但是不能阻止编译器在实现的时候有自定义的设置)
最后终于说到is_pod了,因为使用的是MS的编译器,因此看看MSDN中的解释:
An instance of the type predicate holds true if the type Ty is a scalar type, a POD aggregate type, or a cv-qualified form of one of them, or an array of such a type, otherwise it holds false.
其中还有一个概念“cv-qualified”,看到不同的地方有很多争议,个人觉得这个其实很简单,就是是否可以用const或者volatile修饰。如果可以则是cv-qualified,否则是cv-unqualified。那么你可能会问,还有什么变量不能用const或者volatile来修饰呢?请在下面的例子中查找答案。
因此,最后,希望大家可以理解MSDN中那么短短的一句话。
(额外说明:不同的编译器对于is_pod的实现不一定是相同的,这里是用Visual Studio 2008 SP1的编译器来测试的,而且boost中的实现也针对编译器的不同做了不同的定义)
下面有12个实例来说明
#include <iostream> #include <type_traits>
// === 属于POD的对象 ===
// 没有定义任何数据成员 class pod_yes_1 {};
// 定义了一个元数据类型的公有成员变量 class pod_yes_2 { public: int a; };
// 定义了一个元数据类型以及一个POD对象 class pod_yes_3 { public: float a; pod_yes_2 b; };
// 定义了1个POD对象以及一个迭代的POD对象 class pod_yes_4 { public: float a; pod_yes_2 b; pod_yes_3 c; };
// === 不属于POD的对象 ===
// 从一个POD对象派生 class pod_no_1 : public pod_yes_1 {}; class pod_no_2 : public pod_yes_2{};
// 虚拟继承,更不行了,而且不符合"trivial constructor"的要求 class pod_no_3 : virtual public pod_yes_1 {};
// 定义了一个私有(或者保护)类型的成员变量 class pod_no_4 { int a; };
// 定义了构造函数,虽然是空的,仍然不符合"trivial constructor"的要求 class pod_no_5 { public: pod_no_5(){} };
// 定义了虚函数,不符合"trivial constructor"的要求 class pod_no_6 { public: virtual void foo(){} };
// 父类定义了构造函数:不符合"trivial constructor"的要求 class pod_no_7 : public pod_no_5 {};
// PodRef不是cv-qualified的对象,看到这里知道上面问题的答案了吧。也意味着不符合"trivial constructor"的要求。 // warning C4510: 'pod_no_8' : default constructor could not be generated // warning C4512: 'pod_no_8' : assignment operator could not be generated // warning C4610: class 'pod_no_8' can never be instantiated - user defined constructor required typedef pod_yes_1& PodRef; class pod_no_8 { public: PodRef x;};
template<typename T1> class testpod { public: testpod(){ std::cout<<typeid(T1).name()<<"/tPOD: "<<(std::tr1::is_pod<T1>::value?"yes":"no "); std::cout<<"/tTrivial Constructor: "<<(__has_trivial_constructor(T1)?"yes":"no")<<std::endl; } };
void main() { testpod<pod_yes_1>(); testpod<pod_yes_2>(); testpod<pod_yes_3>(); testpod<pod_yes_4>(); testpod<pod_no_1>(); testpod<pod_no_2>(); testpod<pod_no_3>(); testpod<pod_no_4>(); testpod<pod_no_5>(); testpod<pod_no_6>(); testpod<pod_no_7>(); testpod<pod_no_8>();
pod_yes_1 pod1; const PodRef r1 = pod1; // warning C4181: qualifier applied to reference type; ignored volatile PodRef r2 = pod1; // warning C4181: qualifier applied to reference type; ignored } |
程序输出:
class pod_yes_1 POD: yes Trivial Constructor: yes
class pod_yes_2 POD: yes Trivial Constructor: yes
class pod_yes_3 POD: yes Trivial Constructor: yes
class pod_yes_4 POD: yes Trivial Constructor: yes
class pod_no_1 POD: no Trivial Constructor: yes
class pod_no_2 POD: no Trivial Constructor: yes
class pod_no_3 POD: no Trivial Constructor: no
class pod_no_4 POD: no Trivial Constructor: yes
class pod_no_5 POD: no Trivial Constructor: no
class pod_no_6 POD: no Trivial Constructor: no
class pod_no_7 POD: no Trivial Constructor: no
class pod_no_8 POD: no Trivial Constructor: no