请教一个trait技术的用法

IT_worker 2003-05-20 11:08:33
加精
假如我需要设计一个作用在点上面的范型函数,为了优化此函数需要知道点坐标用的数值的类型。为此我约定用户的点类型value_trait的value_type就是点的坐标类型。
这样我的函数就可以写成:
template<class point>
void foo(point &pt)
{
typedef value_trait<point>::value_type value_type;
//这里的value_type就是point的坐标类型
…………
}
如果用户的某个类型例如CPoint需要调用我得foo函数,那么他需要特例化一下CPoint的value_trait。也就是说用户需要写下:
struct value_trait<CPoint>{
typedef long value_type;
}

现在我设计了一个点的模板类
template<class value_type>
struct point{
value_type x,y;
};
为了让我得point模板类拥有value_trait我该如何写代码?
最笨的一个方法是:
先定义宏:
#define point_value_trait(type) struct value_trait<point<type> >{\
typedef type value_type; }
然后写下:
point_value_trait(int)
point_value_trait(float)
point_value_trait(double)
point_value_trait(short)
…………
但这种方法太让人伤感了。另外的稍微好一点方法是:
先写下
template<class type>
struct value_trait{
typedef typename type::value_type value_type;
}
然后改写我得point模板
template<class type>
struct point{
typedef type value_type;
type x,y;
};
这样一定程度的解决了问题,但是假如另外有仁兄有自己的point的模板类
template<class type>
struct point1{
type x,y;
};
而此point1没有定义value_type,那么用此模板的类型将很难调用我得foo函数。
请问,如果我完全没有写value_trait的缺省实现和没有在point中加入
typedef type value_type这行代码,如何实现我得point模板的value_trait。
...全文
135 点赞 收藏 19
写回复
19 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
IT_worker 2003-05-22
to : plainsong(伤心的风★短歌)
既然你说偏特化的方法在其它的编译器下支持,也就是说你的方法是c++认可的,那么我得问题可以说是结束了。
回复
IT_worker 2003-05-22
to : plainsong(伤心的风★短歌)
看了半天只有你的答案比较对我得意思,其实我也是想用偏例化,可是VC6.0只支持特例化,你的代码
//模板特化:
template<typename _T>
struct value_trait<point<_T> >
{
typedef _T value_type;
}
在VC6.0中是绝对编译不过去的,而我手头又只有VC6.0的编译器,什么时候用gcc试一试。

to : realdreamer(楼主英明,贫僧久仰大名,特来拜见)
你所担心的成员名不一致,对象类型是接口等等在我的设计里不是问题,事实上我定义的点概念需要满足两个相关函数:
template<class point>
value_trait<point>::value_type x(const point &pt);
template<class point>
value_trait<point>::value_type y(const point &pt);
如果是可写的点还需要满足:
template<class point>
void x(const point &pt,value_trait<point>::value_type v);
template<class point>
void y(const point &pt,value_trait<point>::value_type v);
对任意的类型添加这几个函数是相当容易的,这样是否能够满足任意类型?
回复
短歌如风 2003-05-22
我也知道VC6不支持偏特化,所以又写了另一种在VC6中的解决方法。
  使用偏特化的方法我在BCC5.5.1和Dev C++4.9.8.0编译通过了。(BCC5.5.1可以从www.borland.com免费下载)。
回复
realdreamer 2003-05-21
"5555~~~,为什么总说我指责别人呢"
"我是在说realdreamer, 真是冤枉……外面都快下雪了:("

两位也真够逗的:)

昏了, 我还讲了一个重要的东西, 你既要考虑 CPoint, 还得要考虑其他吧, COM 接口咋办? 命名不一致的类咋办?

要不考虑应用, 要提高一点, 讨论得更全面些. 不应该再考虑对于一个 point, 假定为一个只装两个相同类型的容器, 这样你不要定义point::value_type 还应该为它定义point::iteraotr那, 以让 point 适应 stl 算法. 这样不更好?


回复
短歌如风 2003-05-21
“point 本身就是一个泛化的了, 还需要追到它的底层是由什么类型组成的吗?”
我要写一段代码把point中的x和y互换,如果你不知道类型,如何去写?因为互换需要一个临时变量,难道你写一个point类型的变量吗?

  “如果某一算法需要对特殊的 point 做处理, 你能不写重载版本吗?”
做特殊处理时当然需要写重载版本,但不需要特殊处理时呢(这种情况更常见)?比如我上面说过的互换x和y,对于double的和int的point代码都是一样的,只不过是临时变量的类型不同,当然就不需要重载版本了。

  楼主已经说过了“我只是出于纯技术上的兴趣才提出这个问题。”,为什么还要在“过度设计”这个问题上纠缠不清呢?
回复
短歌如风 2003-05-21
我没有说你指责别人啊——我是在说realdreamer。真是冤枉……外面都快下雪了:(

我觉得用继承来实现还是不好的,这样就要求使用都不得不为他要用到的类都写一个衍生类,并且知道你的函数要用到value_type类型定义这样一个实现事实。如果实现有所修改……

  正规方法还是使用偏特化。我写的第二种方法:用接受无用指针类型参数的重载函数的方法只是在不支持偏特化的编译器(如VC6)中的一个代替方案。
回复
realdreamer 2003-05-21
这个问题的最佳解法我想只能由楼主来判断.

我所提出的是针对楼主 "为了优化此函数需要知道点坐标用的数值的类型" 这样一个需要.

既然用模板包装了 point, point 本身就是一个泛化的了, 还需要追到它的底层是由什么类型组成的吗? 对于容器而言, 一个 point 就是一个 value_type 了. 至此, 我仍然认为是一种过渡设计, 这种过渡设计是源于形式化了. 泛化是为了应用的, 不是看起来一致(都有value_type)

"难道要所有的函数都写这么多的重载版本吗"
如果某一算法需要对特殊的 point 做处理, 你能不写重载版本吗? 如果不需要特殊处理, 我的方案后两个foo函数的特化也不需要了.

相反, 我觉得如果像楼主这么考虑(MFC 的 CPoint 没有 value_type), 那还将面临一些问题, 如果一个 COM 的 Point 出现你怎么办? 它没有成员的, 只有函数, 如果另外一个库中的 point 在 x, y 的命名上与你不同, 你的算法能不能成功? 在成员前加下划线(像这样 _x)是很常见的. 你怎么办?


你要是真要考虑这么多, 你就不用设计了. 本来各个库的设计都是不同的, 你想要完全统一是不行的.
回复
hpho 2003-05-21
5555~~~,为什么总说我指责别人呢。我的确没了解楼主的应用。开始以为他是针对单一类型,后来才知他是针对容器

plainsong:这样应该可以吧.

template<class T>
struct XCArray: public CArray<T,T>{
typedef T value_type;
};

XCArray<CPoint> p;
foo(p);
回复
短歌如风 2003-05-21
我觉得楼上并没有真正了解Traits的作用,这个value_trait的作用是可以让使用这些类(point、Array、std::vector、etc.)的函数可以知道相关的一个类型,而foo只是需要知道这样一个类型的函数,还会有其它函数同样需要知道这个信息,难道要所有的函数都写这么多的重载版本吗?
  至于针对楼主的实际问题,是不是“过度设计”,我不太了解楼主的实际应用情况,无法评论。
  楼上对hpho的指责也是不正确的。偏特化正是解决这个问题的最佳手段。不过由于VC6不支持,所以需要绕一些弯路。
回复
realdreamer 2003-05-21
type_trai 不是像你这么用的.
你确实有过度设计.
直接特化 foo 函数不可以吗?搞这么复杂.
你的点会有指针吗?x,y必须是值.


hpho() 是没理解贴主的意思. 你函数里定义 value_type 不能帮助特化函数.
取得value_type并没有什么特别的作用!




template<typename T>
struct point {
T x;
T y;
};

template<class point>
void foo(point& p)
{

};

template<>
void foo(point<float>& p)
{

}

template<>
void foo(CPoint& p)
{

}

像这样, 你可以对由float 组成的point, 对 MFC 的CPoint 有特殊处理.

回复
hpho 2003-05-21
楼上答了。偏特化。
template<class T>
struct value_trait;
struct Default;

struct value_trait<Default>{ // 普通特化
typedef long value_type;
};

template<class T> // 偏特化
struct value_trait<CArray<T,T&> >{
typedef T value_type;
};
还有并不是指责你过渡设计,而是想说你会不会想的东西离实际远了点。还有过渡设计我觉得不是贬义:)。
回复
短歌如风 2003-05-21
hpho()说“TRAIT不是向外拓展类型的手段”未免绝对化了,事实上我们经常使用Traits技术来确定类型。事实上在STL中的算法中很多都用到了这种技术来确定iterator的value_type,并且对指针类型作了特化。
  不过这种特化方法在vc6中不支持,必须改用函数重载方法实现:

#include <vector>
#include <iostream>

template<class value_type>
struct point{
value_type x,y;
};

template <typename _T>
struct value_trait
{
typedef typename _T::value_type value_type;
};

template <typename _T, typename _V>
void Foo(_T Param, _V*)//第二个参数不使用,只是为了确定类型。
{
_V a;
std::cout<<typeid(a).name()<<std::endl;
};

template <typename _T>
_T * get_value_type(const std::vector<_T>&)
{
return NULL;
}

template <typename _T>
_T* get_value_type(const point<_T> &)
{
return NULL;
}

template <typename _T>
void foo(_T Param)
{
Foo(Param, get_value_type(Param));
}


int main(int argc, char* argv[])
{
foo(std::vector<double>());
foo(point<int>());
return 0;
}

这样你就需要为vector、deque等所有的参数类型定义get_value_type函数,这时可以用宏来实现:
#define DEF_VALTYPE_FUN(X) \
template <typename _T>\
_T * get_value_type(const X<_T>&)\
{\
return NULL;\
}

然后:
DEF_VALTYPE_FUN(std::vector)
DEF_VALTYPE_FUN(std::deque)
...
虽然还是要使用宏,但是比
point_value_trait(int)
point_value_trait(float)
point_value_trait(double)
point_value_trait(short)
…………
这样写要方便多了。

又及:听说下一个版本的VC将全面支持标准的类模板部分特化。
回复
短歌如风 2003-05-21
template<class point>
void foo(point &pt)
{
typedef value_trait<point>::value_type value_type;
}

template<class value_type>
struct point{
value_type x,y;
};

template <typename _T>
struct value_trait
{
typedef typename _T::value_type value_type;
}

//模板特化:
template<typename _T>
struct value_trait<point<_T> >
{
typedef _T value_type;
}
回复
IT_worker 2003-05-21
我设计value_trait模板的目的是通过value_trait<point_type>::value_type推断出point_type采用的坐标类型。缺省的实现要求point_type拥有value_type的成员类型,对于没有value_type的类型可以特例化它的value_trait,但如何特例化没有value_type的模板类就是我的问题所在。
例如可以通过上面的value_trait可以得到所有stl容器如vector,list,set,map等的value_type。但是确得不到MFC容器CArray<type,type&>的value_type,而我又希望同一个value_trait能得到CArray<type,type&>的value_type。如果此CArray是某个具体的类型例如:
CArray<CPoint,CPoint&>那么问题可以通过特殊化
struct value_trait<CArray<CPoint,CPoint&> >{
typedef CPoint value_type;
};
做到。当我希望上面的特殊化方法对整个CArray模板都有效,该如何做到。
请不要建议我修改foo因为foo只是value_trait的一个应用,更不要指责我过渡设计,因为我只是出于纯技术上的兴趣才提出这个问题。
回复
hpho 2003-05-21
使用trait的意义是针对某些类型做某些操作,可以说是为template<class type>class point{};里的type限定一个范围,所以TRAIT不是向外拓展类型的手段。
如:
template<class type>
struct value_trait;

template<>
struct value_trait<int>{
typedef int value_type;
static dosomething(); // 提供一些int类型的特殊操作
};

template<>
struct value_trait<double>{
typedef double value_type;
static dosomething(); // 提供一些double类型的特殊操作
};

template<class type, class vt = value_trait<type>/**/>
struct point{
typedef vt::value_type value_type;
type x,y;
};
point的被限定为int或double

如果要使foo()能使用其它人写的point的那就应该使用继承,即别人从你的POINT中继承下来。如:
template<class type>
struct point3d: public point<type>{
type x, y, z;
};
这样就能使类型有向外拓展的能力.
回复
ixMind 2003-05-21
提供两种方案
1. 给foo增加一个参数(表示value_type)
template<class point, class val_type>
void foo(point &pt)
{
typedef val_type value_type;
//这里的value_type就是point的坐标类型
…………
}

2.将foo的类型参数改为模板类(这种方式好像是叫做template template programming)
template<template<class val_type> class point>
void foo(point &pt)
{
typedef val_type value_type;
//这里的value_type就是point的坐标类型
…………
}

当然,如果两个foo能共存的话就更好了
也可考虑写成
template<template<class val_type> class point, class value_type=val_type>
void foo(point &pt)

我对template也不太熟,上述方案仅供参考
回复
hpho 2003-05-21
我觉得你有点过度设计了。
foo函数跟就不用value_trait
template<class point>
void foo(point &pt){
typedef typename point::value_type value_type;
}
这样不就行了吗?
你认为提供回重用的代码,别人就可以用吗?重用是要提供文档的。即然有文档就不必怕别人不按一定条件书写struct point啦。
所以我觉得你不需要考虑这么多
回复
IT_worker 2003-05-21
从现实的角度来说,我的第二个解决方法已经很好的解决问题了,因为其它的人只会用我的point模板,最多用一下CPoint这样的具体的类。
我在这里提出这个问题的用意是类似这样的问题技术上如何解决。
回复
ciml 2003-05-21
有点高深
我一直对trait技术认识不清
其实有时候不要搞那么复杂,你可做一些约定,比如让point1必须定义value_type
这些东西可以在开发中以文档形式约定好。
回复
相关推荐
发帖
工具平台和程序库
创建于2007-09-28

2.4w+

社区成员

C/C++ 工具平台和程序库
申请成为版主
帖子事件
创建了帖子
2003-05-20 11:08
社区公告
暂无公告