- 浏览: 208490 次
- 性别:
- 来自: 重庆
-
文章分类
最新评论
C++类型萃取技术
- 博客分类:
- c++
Traits技术可以用来获得一个 类型 的相关信息的。
首先假如有以下一个泛型的迭代器类,其中类型参数 T 为迭代器所指向的类型:
template <typename T>
class myIterator
{
...
};
当我们使用myIterator时,怎样才能获知它所指向的元素的类型呢?我们可以为这个类加入一个内嵌类型,像这样:
template <typename T>
class myIterator
{
typedef T value_type;
...
};
这样当我们使用myIterator类型时,可以通过 myIterator::value_type来获得相应的myIterator所指向的类型。
现在我们来设计一个算法,使用这个信息。
template <typename T>
typename myIterator<T>::value_type Foo(myIterator<T> i)
{
...
}
这里我们定义了一个函数Foo,它的返回为为 参数i 所指向的类型,也就是T,那么我们为什么还要兴师动众的使用那个value_type呢? 那是因为,当我们希望修改Foo函数,使它能够适应所有类型的迭代器时,我们可以这样写:
template <typename I> //这里的I可以是任意类型的迭代器
typename I::value_type Foo(I i)
{
...
}
现在,任意定义了 value_type内嵌类型的迭代器都可以做为Foo的参数了,并且Foo的返回值的类型将与相应迭代器所指的元素的类型一致。至此一切问题似乎都已解决,我们并没有使用任何特殊的技术。然而当考虑到以下情况时,新的问题便显现出来了:
原生指针也完全可以做为迭代器来使用,然而我们显然没有办法为原生指针添加一个value_type的内嵌类型,如此一来我们的Foo()函数就不能适用原生指针了,这不能不说是一大缺憾。那么有什么办法可以解决这个问题呢? 此时便是我们的主角:类型信息萃取技术Traits 登场的时候了
我们可以不直接使用myIterator的value_type,而是通过另一个类来把这个信息提取出来:
template <typename T>
class Traits
{
typedef typename T::value_type value_type;
};
这样,我们可以通过 Traits<myIterator>::value_type 来获得myIterator的value_type,于是我们把Foo函数改写成:
template <typename I> //这里的I可以是任意类型的迭代器
typename Traits<I>::value_type Foo(I i)
{
...
}
然而,即使这样,那个原生指针的问题仍然没有解决,因为Trait类一样没办法获得原生指针的相关信息。于是我们祭出C++的又一件利器--偏特化(partial specialization):
template <typename T>
class Traits<T*> //注意 这里针对原生指针进行了偏特化
{
typedef typename T value_type;
};
通过上面这个 Traits的偏特化版本,我们陈述了这样一个事实:一个 T* 类型的指针所指向的元素的类型为 T。
如此一来,我们的 Foo函数就完全可以适用于原生指针了。比如:
int * p;
....
int i = Foo(p);
Traits会自动推导出 p 所指元素的类型为 int,从而Foo正确返回。
自从C++中引入了template后,以泛型技术为中心的设计得到了长足的进步。STL就是这个阶段杰出的产物。STL的目标就是要把数据和算法分开,分别对其进行设计,之后通过一种名为iterator的东西,把这二者再粘接到一起。设计模式中,关于iterator的描述为:一种能够顺序访问容器中每个元素的方法,使用该方法不能暴露容器内部的表达方式。可以说,类型萃取技术就是为了要解决和iterator有关的问题的,下面,我们就来看看整个故事。
应该说,迭代器就是一种智能指针,因此,它也就拥有了一般指针的所有特点——能够对其进行*和->操作。但是在遍历容器的时候,不可避免的要对遍历的容器内部有所了解,所以,设计一个迭代器也就自然而然的变成了数据结构开发者的一个义务,而这些iterators的表现都是一样的,这种内外的差异,对用户来说,是完全透明的,
第一部分 为什么要有萃取技术
既然是一种智能指针,iterator也要对一个原生指针进行封装,而问题就源于此,当我们需要这个原生指针所指对象的类型的时候(例如声明变量),怎么办呢?
Case1 对于函数的局部变量
这种情况我们可以采用模版的参数推导,例如:
template <class T> void func(T iter)
如果T是某个指向特定对象的指针,那么在func中需要指针所指向对象类型的变量的时候,怎么办呢?这个还比较容易,模板的参数推导机制可以完成任务,如下:
template <class T, class U>
void func_impl(T t, U u) {
U temp; // OK, we’ve got the type
// The rest work of func…
}
template <class T>
void func(T t) {
func_impl(t, *t); // forward the task to func_impl
}
通过模板的推导机制,我们轻而易举的或得了指针所指向的对象的类型,但是事情往往不那么简单。例如,如果我想把传递给func的这个指针参数所指的类型作为返回值,显然这个方法不能凑效了,这就是我们的case 2。
Case2 对于函数的返回值
尽管在func_impl中我们可以把U作为函数的返回值,但是问题是用户需要调用的是func,于是,你不可能写出下面的代码:
template <class T, class U>
U func_impl(T t, U u) {
U temp; // OK, we’ve got the type
// The rest work of func…
}
template <class T>
(*T) func(T t) { // !!!Wrong code
return func_impl(t, *t); // forward the task to func_impl
}
int i =10;
cout<<func(&i)<<endl; // !!! Can’t pass compile
红色的部分概念上如此正确,不过所有的编译器都会让你失望。这个问题解决起来也不难,只要做一个iterator,然后在定义的时候为其指向的对象类型制定一个别名,就好了,像下面这样:
template <class T>
struct MyIter {
typedef T value_type; // A nested type declaration, important!!!
T* ptr;
MyIter(T* p = 0) : ptr(p) {}
T& operator*() const { return *ptr; }
};
而后只要需要其指向的对象的类型,只要直接引用就好了,例如:
template <class I>
typename I::value_type func(I iter) { return *iter; }
很漂亮的解决方案,看上去一切都很完美。但是,实际上还是有问题,因为func如果是一个泛型算法,那么它也绝对要接受一个原生指针作为迭代器,但是显然,你无法让下面的代码编译通过:
int *p = new int(52);
cout<<func(p)<<endl; // !!!Is there a int::value_type?? Wrong Code here
我们的func无法支持原生指针,这显然是不能接受的。此时,template partial specialization就派上了用场。
Solution :template partial specialization是救世主
既然刚才的设计方案仍不完美,那我们就再加一个间接层,把智能指针和原生指针统统的封装起来。在讨论之前,先要澄清一下template partial specialization的含义。所谓的partial specialization和模板的默认参数是完全不同的两件事情,前者指的是当参数为某一类特定类型的时候,采用特殊的设计,也就是说是“针对template参数更进一步的条件限制所设计出来的一个特化版本”;而默认参数则是当不提供某些参数的时候,使用的一个缺省。
参考:partial specialization的语法
Template <typename T> class C<T*> {} // 为所有类型为T*的参数而准备的特殊版本
好了,下面我们就找一个专职的负责人,用来封装迭代器封装的对象类型。首先,把我们刚才的MyIter重新包装一下:
template <class I>
struct iterator_traits {
Typedef I::value_type value_type;
}
现在,我们的func又有了新的面貌:
template <class I>
typename iterator_traits<I>::value_type func(I ite) {
return *ite;
}
尽管这次我们的函数返回值的长度有些吓人,但是,我们的确为原生指针找到了好的解决方案。只要为原生指针提供一个偏特化的iterator_traits就OK了。如下:
template <class I>
struct iterator_traits<T*> {
typedef T value_type;
};
下面,我们终于可以让func同时支持智能指针和原生指针了:
template <class I>
struct iterator_traits {
Typedef I::value_type value_type;
}
template <class T>
struct iterator_traits<T*> {
typedef T value_type;
};
template <class I>
typename iterator_traits<I>::value_type func(I ite) {
return *ite;
}
int main() {
MyIter<int> iter = new int(520);
int *p = new int(520);
// This time the following two statements will success
cout<<func(iter)<<endl;
cout<<func(p)<<endl;
return 0;
}
但是,我们离最后的成功还有最后一步,如果,我们需要声明一个value_type类型的左值,但是却给iterator_traits传递了一个const int*,显然结果有问题,于是,为const T*也另起炉灶,准备一份小炒:
template<class T>
struct iterator_traits<const T*> {
typedef T value_type;
}
OK ,现在万事大吉,无论是正宗迭代器,原生指针,const原生指针,我们都可以利用iterator_traits萃取出其封装的对象的类型,萃取技术由此而来。
发表评论
-
C++的原子操作
2012-12-20 17:43 4697在多进程(线程)访问资源时,能够确保所有其他的进程(线程 ... -
匿名namespace的作用以及它与static的区别
2012-12-20 17:24 1865一。匿名namespace的作用 在C语言中,如果我们 ... -
数值压缩存储方法Varint
2012-12-19 14:35 881转自:http://www.cnblogs.com/smark ... -
TypeList
2012-12-19 13:49 1162转自:http://blog.csdn.n ... -
template <unsigned int N>
2012-12-19 11:51 1508详见:http://stackoverflow.com/ ... -
二维指针*(void **)的研究(uC/OS-II案例)
2012-12-19 22:20 3308原文 : http://blog.csdn ... -
多级指针和链表
2012-12-18 22:28 0如果看到一个声明:t ... -
理解*(void**)b
2012-12-18 22:03 0#include <stdio.h> ... -
STL标准库:Allocator能做什么
2012-12-18 20:10 0The Standard Librarian: Wha ... -
三种的allocator实现源代码的对比
2012-12-18 19:55 1353转自:http://blog.csdn.net ... -
结构体内变量相对便宜与list_entry()宏
2012-12-18 17:59 963#define list_entry(ptr, t ... -
声明与函数、函数指针---(*(void (*)( ) )0)( ) 解析
2012-12-18 17:33 1123概述 在很 ... -
c++模板(类型依赖)说明例子
2012-12-18 16:57 1173#include <iostream> # ... -
C++中三种new的用法
2012-12-18 16:44 1845我评价自己的C++水平还未入门的确不够准确,应该是远远未 ... -
C++,永久改变你写异常安全代码的方式(神奇的Loki::ScopeGuard)
2012-12-17 20:19 2528作者:Andrei Alexandrescu and P ... -
C++的make_pair函数
2012-12-17 17:19 3538Pairs C++标准程序库中凡是“必须返回两 ... -
C++的explicit构造函数
2012-12-13 15:59 684按照默认规定,只有一个参数的构造函数也定义了一个隐式转换 ...
相关推荐
C++类型萃取是一种编译时编程技术,它允许程序员在代码编译阶段获取并分析类型信息,并根据这些信息决定程序的行为。类型萃取通过模板特化和SFINAE(SubstitutionFailureIsNotAnError)等技术,使得程序员能够控制...
"C++内容提取工具"可能是一个利用C++编写的程序,旨在帮助用户从不同类型的文件中提取内容,例如文本文件、数据库文件或者特定格式的二进制文件。这样的工具在数据处理、文本挖掘、日志分析等领域有着广泛的应用。 ...
在C++编程语言中,通用类型的实现通常依赖于模板或者智能指针等技术。这里提到的"virtual_type"概念,似乎是一种自定义的通用类型,它允许存储和传递任何类型的数据,这与C++标准库中的`std::any`有类似之处。`std::...
《Visual C++小波变换技术与工程实践》一书由靳济芳编著,是一部深入探讨如何使用Visual C++实现小波变换理论及其在实际工程中的应用的专著。本书结合了理论与实践,旨在帮助读者掌握小波变换的核心概念,并能够运用...
《Visual C++小波变换技术与工程实践》一书由靳济芳编著,是一部深入探讨如何在实际工程中应用小波变换的专著。本书结合了理论与实践,为读者提供了丰富的Visual C++编程经验,使读者能够利用C++语言进行高效的小波...
P2P网络技术原理与C++开发案例这本书涉及了P2P网络的基础理论和实际C++开发案例。P2P网络技术是计算机网络领域中的一个重要技术,它允许不同的个人电脑直接互联,共享资源,如文件、存储空间和CPU处理能力,而不必...
《Visual C++数字图像识别技术典型案例——求是科技》是一份深入探讨图像处理和模式识别技术的教程,特别强调在VC++ 6.0开发环境中的应用。这份资源旨在为学习者提供一个实用的平台,通过实例代码来理解和掌握这些...
在编程领域,主范式(Prime Normal ...总的来说,C++中提取主范式的程序设计不仅关乎数据库理论的运用,更关乎编程实践中的良好习惯。理解和应用这些原则,可以帮助开发者编写出更高质量、更易于理解和维护的C++程序。
在C++中实现TLV编解码是一项技术挑战,涉及到数据结构设计、内存管理以及高效的数据操作。 首先,`tlv`是这个项目的重点,它代表了C++实现的TLV编解码功能。项目可能包含了一个名为`tlv`的库或者模块,提供了处理...
在C++中,H264文件的逐帧提取是一个涉及视频编码和解码过程的复杂任务。H264,全称为高级视频编码(Advanced Video Coding),是一种高效的视频压缩标准,广泛应用于数字电视、互联网视频和移动设备等领域。本项目...
《Visual C++音视频编解码技术及实践》是一本深度探讨使用Visual C++进行音视频处理的专业书籍,结合了理论知识与实际操作,旨在帮助读者掌握音视频编解码的核心技术。书中不仅包含了丰富的理论内容,还提供了实战...
在《Visual C++数字图像识别技术典型案例》这个资源中,我们深入探讨了使用Microsoft Visual C++(VC++)进行图像处理和识别的核心概念和技术。这个压缩包包含了与图像处理相关的多个章节,以及一个名为"光盘使用...
书名:《Visual C++串口通信技术详解》(机械工业出版社.李景峰.杨丽娜.潘恒) PDF格式扫描版,全书分为16章,共368页。2010年6月出版。 内容简介 本书介绍如何利用Visual C++集成开发环境进行串口通信程序开发。书...
本话题聚焦于如何在C++环境中,特别是在使用Visual C++ 6.0这一经典集成开发环境(IDE)时,实现从对话框中提取颜色的RGB值。RGB值是颜色的一种表示方式,它由红色(Red)、绿色(Green)和蓝色(Blue)三个通道的...
从标题和描述中,我们可以提取出几个与学习C++语言相关的知识点,这些知识点对于希望深入理解C++编程语言的读者来说至关重要。 首先,这本书的标题《C++探索:程序员的C++入门教程》指出了其面向的人群是程序员,...
根据提供的文件信息,以下是关于C++编程语言的相关知识点: C++语言的前世今生: 1. C++与C++之父:C++语言是由...通过学习这些知识,读者能够掌握C++语言的核心特性和编程技术,为进一步的软件开发打下坚实的基础。
在C++中,读取Excel数据通常涉及到使用OLE Automation(对象链接和嵌入自动化)技术,这是一种允许不同应用程序之间交互的方式。在这个特定的例子中,我们使用Microsoft Visual Studio 2003 MFC(Microsoft ...
在进行MFCC特征提取时,还需要考虑一些其他因素,如采样率、帧长、帧移、窗函数类型、梅尔滤波器的数量等,这些参数的选择会影响最终提取的MFCC特征的质量和适用性。因此,实际使用时,可能需要针对具体任务进行参数...
在C++程序设计中,首先要理解的是C++的基本语法,包括变量声明、数据类型(如整型、浮点型、字符型等)、运算符、流程控制(如if-else语句、switch-case语句、循环结构for、while和do-while)以及函数的使用。...