小众知识

首页 > 正文

【<专题讨论>[模板模式]<2>】成员模板和返回值重载

(1)成员模板
     提供了一种类的成员函数的无限可能性
(2)返回值重载
     使得我们的表达式调用语义依赖与使用上下文。

下面是我写的一个例子:

#include 
#include 

struct Return_overload
{
  FARPROC data;

  Return_overload(FARPROC data_)
    : data(data_)
    {}

  template 
  operator T () const
  {
    return (T)data;
  }
};

struct Library
{
  HMODULE handle;
  Library(char const* name)
    : handle(LoadLibrary(name))
  {
    if (handle == NULL)
      throw std::bad_exception();
  }
  ~Library()
  {
    FreeLibrary(handle);
  }

  Return_overload get_func(char const* name)
  {
    return GetProcAddress(handle, name);
  }
};

int main()
{
  Library lib("c:\\test.dll");
  if (int (*show)(const char*) 
      = lib.get_func("_db2_show_sql_form")
     )
    show("c:\\db2sql\\define.ini");

  // new : 

  // old : 
  // int (*show)(const char*) = 
  //   (int (*)(const char*))GetProcAddress(...);
       ^^^^^^^^^^^^^^^^^^^^^

  // int (*show)(const char*) =
  //                        lib.get_func("_db2_show_sql_form");
  //   ^^^^^^^^^^^^^^^^^^^^^
  //   此处没有了上面的返回值类型转换。

  return 0;
}

注意^^^^^^^^^^部分,由于我们的成员模板和返回值重载
的使用,使得我们可以少写一个类型转换。

希望大家:
(1)探讨一下这个机制的一般化
(2)提供一些其他的应用代码例子
(3)弊端
我们在这里共同探讨一下,共同进步。  

 ttlb 发表于:2003-10-29 14:38:01
得分:0 

up 

 yjh1982 发表于:2003-10-29 14:55:16
得分:20 

class A
{
public:
template
A& operator=(const T &in){std::cout<<"in left\n";return *this;}
};
int main()
{
A a;
a=1;
a=1.1;
a="foo";
  return 0;
}
不知道有什么用:( 

 xueweizhong 发表于:2003-10-29 15:06:27
得分:0 

>to yjh1982(血精灵)
你的这个类里实现了成员模板的赋值操作。
也就是可以将无限多种类型的值赋值到A上。
那么A就象一个能包容万象的容器了。
按照这个概念上来说:

A类似于boost::any:
   本身不是模板类,却可以往里放任何类型。
   在需要的时候再取出。

但如果A不保存信息,象你这里写得那样,纯粹
是实现了一个功能:
  std::cout << ...
那么A更象一个模板函数的作用。

不知道我说得对不对,请继续指教。 

 plainsong 发表于:2003-10-29 15:11:22
得分:0 

“少写一个类型转换”并不是了不起的优点,我觉得这种方法优点真正体现在和(偏)特化结合使用的时候。这时我们可以对到某些类型的转换时多做些事情(比如溢出检查)或禁止某些转换。比如:
struct return_overload
{
  int data;
  return_overload(int init_data):data(init_data){};
  template
  operator T() const {return T(data);}
  template<>
  operator unsigned int()const(assert(data >= 0); return static_cast(data);}
};
另一种方式是把函数调用推迟到发生类型转换时,结合特化方法,对某些类型的返回值调用不同的方法:
struct return_overload
{
  unsigned int data;
  return_overload(unsigned int init_data):data(init_data){};
  template
  operator T() const {return T(double(data)/2);}
  template<>
  operator unsigned int () const {return data >> 1;}
}; 

 plainsong 发表于:2003-10-29 15:15:37
得分:20 

对不起,第二种方式没写完。
函数:
return_overload binary(unsigned int data)
{
  return return_overload(data);
}
调用时:
double a;
unsigned int b;
a = binary(123);//相当于a = double(123)/2;
b = binary(123);//相当于b = 123 >> 1; 

 xueweizhong 发表于:2003-10-29 15:34:32
得分:0 

>to plainsong(短歌)
板主大人又给我门加上了一维:
  ----成员模板的特殊化。
这让又我想起来那些人说:
 特殊化总能被找到,而重载不在作用域内就不能被找到。
 我对此总有些疑惑...

 plainsong 发表于:2003-10-29 16:00:47
得分:20 

一个非常奇怪的现象:
我试了试第二种方式:
struct return_overload
{
  unsigned int data;
  return_overload(unsigned int init_data):data(init_data){};
  template
  operator T() const {std::cout << "operator T()" << std::endl;return T(double(data)/2);}
  template<>
  operator unsigned int () const {std::cout << "operator unsigned int()" << std::endl;return data >> 1;}
};
return_overload binary(unsigned int data)
{
  return return_overload(data);
}

int main(int argc, char* argv[])
{
double a;
unsigned int b;
a = binary(123);//相当于a = double(123)/2;
b = binary(123);//相当于b = 123 >> 1;
std::cin.get();
return 0;
}
输出为:
operator T()    double
operator unsigned int()
而当把operator unsigned int 前的template<>去掉后(我本以为结果会一样),结果为:
operator T()    double
operator T()    unsigned int
在有operator unsigned int 的情况下,向unsigned int的转换仍然调用了模板版本的转换操作符,不知道在其它编译器下是什么情况,标准中又是如何规定的。 

 yjh1982 发表于:2003-10-29 16:13:49
得分:20 

短歌发现的真叫人沮丧 

 plainsong 发表于:2003-10-29 16:17:50
得分:20 

我用的编译器是bcc 5.6(BCB6)和bcc 5.6.4(BCBX) 

 plainsong 发表于:2003-10-29 16:24:39
得分:20 

在Dev C++ 4.9.8.0中第一种情况无法通过编译,而第二种情况结果是:
operator unsigned int()
operator unsigned int()
竟然没有调用模板版本。

在VC6中只非特化版本的情况下都无法编译(INTERNAL COMPILER ERROR) 

 yjh1982 发表于:2003-10-29 16:38:31
得分:20 

Dev C++ 4.9.8其实烂得很:
template
class A
{
typedef typename T type;
};
要去掉typename才能成功@_@ 

 plainsong 发表于:2003-10-29 16:38:42
得分:20 

看来对于“成员模板特化”不同的编译器有不同的动作,看来最可靠的方法是用“重载”来处理了,只要让所有的“模板参数”都出现在“函数参数列表”中就行了:
struct return_overload
{
  unsigned int data;
  return_overload(unsigned int init_data):data(init_data){};

  template 
  T do_type_cast(T*) const {return T(double(data)/2);}//模板成员处理一般情况

  unsigned int do_type_cast(unsigned int *)const{return data >> 1;}//对特殊情况进行重载

  template
  operator T() const {return do_type_cast((T*)0);}
};

 xueweizhong 发表于:2003-10-29 16:49:12
得分:0 

>to plainsong
首先发现的是一个错误
struct return_overload
{
  ...
  template<>
  ^^^^^^^^^^
  operator unsigned int () const
  {...}
};

模板的显示特殊化只能出现在名字空间SCOPE中。
所以你需要把它写到类外边。

其次
将template <>去掉后
g++, CC, VC.net的输出都是
operator unsigned int
operator unsigned int

只有BCB输出为
operator T()
operator T()

这样一个细节应该是BCB又错了,为什么?

 bbcallen 发表于:2003-10-29 16:57:10
得分:0 

vc.net两种都是
operator unsigned int()
operator unsigned int()
抓狂了! 

 yjh1982 发表于:2003-10-29 17:00:43
得分:0 

看来是一种优化:
G++它们可以防止代码膨胀.
不过我不喜欢,这会丢失精度嘛! 

 Jinhao 发表于:2003-10-29 17:15:44
得分:20 

我把你的代码在Dev-c++中编译,得到的结果是
operator T()
operator T()
template <>要写成template 可以通过编译
如果把template 去掉,结果就是
operator unsigned int()
operator unsigned int()

在C++标准这样说的,未被使用的template functions 的语法分析层级由编译器自行决定。

如果改为
struct return_overload
{
  unsigned int data;
  return_overload(unsigned int init_data):data(init_data){};
 
  operator double() const {std::cout << "operator T()" << std::endl;return double(double(data)/2);}
  
  operator unsigned int () const {std::cout << "operator unsigned int()" << std::endl;return data >> 1;}
};
如果这样将会得到期望的答案。

 plainsong 发表于:2003-10-29 18:13:07
得分:0 

我觉得这里理解有些不一致,我觉得一个名字空间中的类的声明部分应该属于这个名字空间的作用域内,这样理解的话BCB中允许的对成员模板的特化方式应该是正确的。而且这样比较方便,否则对类模板A的嵌套类模板B的成员函数模板C的特化就不得不这样:
template template  template<>
void A::B::C()...
未免太麻烦了。

不过为了兼容性来说看来这种特化方法无论是否符合标准都不应该使用了,同时为了方便用stub overloaded member function更好一点,还能避免VC在模板参数不出现在函数参数列表中时的函数签名Bug。
说到这里,哪位有VC7,能不能帮我试试下面这个程序,看看VC7是不是还有这个Bug?
#include 
#include 

template 
Test(){std::cout << typeid(T).name() <
int main(int argc, char* argv[])
{
Test();
Test();
std::cin.get();
return 0;
}

而第二个问题我觉得从模板实例化规则来讲无论是都实例化模板还是都调用非模板版本都应该是错的。不知道关于模板实例化规则对操作符成员是否有特殊规则,不过类型转换操作符可能是唯一一种既不需要参数又不需要显示实例化语法来确定类型的成员,比较特殊一些。 

 xueweizhong 发表于:2003-10-29 19:56:47
得分:0 

我用的版本是:
Microsoft Visual C++ .NET   69514-335-0000007-18549
Microsoft 开发环境2003 version 7.1.3091

(我单位的机器上也装了较早版本的.net2003,下午我试过的结果很不正常。
  这里都不在用这个环境)

在这个环境里,一切都向着预期的那样。
当然:
    需要把explicit-模板特殊化写在名字空间内。

你的程序我都试过了,只要把explicit-specialization
写在名字空间里,没有任何问题。
而且执行结果都很正常:
输出为:
operator T()    double
operator unsigned int()
(无论有无template<>,结果都这样)

另外下面的代码中:
template template  template<>
void A::B::C()...
导致了更严重的语法问题:
template explicit-specialization必须出现在名字空间scope,
现在这里嵌套在模板里,所以发生了更多的语法错误。

关于这个问题我在以前的某个帖子里和人讨论过,正因为那个原因,
才在我的notebook上装了这个较好版本的vc7.1。

上帖中你贴的VC6.0中有问题的代码在这里当然顺利编译通过。
所以至少现在不需要担心VC的不可移植性问题。

建议你也去装一个。 

 xueweizhong 发表于:2003-10-29 20:01:38
得分:0 

>to plainsong:
关于显式模板特殊化必须出现在namespace-scope的问题,
请看我以前讨论过的这个贴:
http://expert.csdn.net/Expert/topic/2339/2339229.xml?temp=.7856409 

 plainsong 发表于:2003-10-29 20:05:31
得分:0 

我贴上来的VC6有问题的代码并不是编译不过,而是运行不正确,它显示了两行“int”。这是VC6的一个BUG,不过我的一个朋友正好利用了这个BUG,我担心他的代码会在VC7中出问题。我这里没有VC7,所以试不了。

 plainsong 发表于:2003-10-29 20:13:11
得分:0 

我并不是不同意模板特化必须出现在namespace-scope(准确地说,应该是和非特化版本相同的namespace-scope),只是对于namesapce的class声明域与namespace-sope的关系有不同的理解。不过通常对错是没有意义的,流行才是最重要的。所以我觉得关于这个问题已经不必再讨论了。我们已经跑题太远了,应该回到主主题上来。 

 xueweizhong 发表于:2003-10-29 20:27:35
得分:0 

>to plainsong:
我帮你试了一下,我的版本的结果是:
double
int

另外:模板部分特殊化(不是显式特殊化)
是可以出现在class-scope,甚至
template-class scope的。

当然,言归正转,我们还是继续讨论返回值重载的各种
可能性.... 

 Wolf0403 发表于:2003-10-30 10:46:52
得分:0 

如果真的把这个 Return_overload 泛化出来,和前一阵讨论的 any_cast 搭配一下,还是很帅的,哈哈

template 
struct Return_Overload
{
Return_Overload(const _Ty1& _data)
: m_data(_data)
{}

template 
operator _Ty2 () const
{
return _Ty2 (m_data);
}
private:
_Ty1 m_data;
};

template 
Return_Overload<_Ty> any_cast(const _Ty& _data)
{
return Return_Overload<_Ty>(_data);
}

class T
{
public:
T(int _i):i(_i){}
operator int() const {cout << __LINE__ << endl; return i;}
private:
int i;
};

int main()
{
T t = T(3);
int i = any_cast(t);
cout << i << endl;
}

不过,正如老大说的:“少写一个类型转换”并不是了不起的优点 

 Wolf0403 发表于:2003-10-30 10:54:55
得分:0 

如果是为了和这个 any_cast 搭配,那么可以继续简化一下:
template 
struct Return_Overload
{
Return_Overload(const _Ty1& _data)
: m_data(_data)
{}

template 
operator _Ty2 () const
{
return _Ty2 (m_data);
}
private:
const _Ty1& m_data; // 再不需要本地拷贝,整个一个 adapter
}; 

 xueweizhong 发表于:2003-10-30 15:41:38
得分:0 

>Wolf0403(完美废人) 
这个东西好象不能用。你再考虑一下为什么? 

 plainsong 发表于:2003-10-30 15:50:12
得分:0 

如果是为了实现any_cast,用类模板结合特化会更好一些。这个问题我和widewave简单讨论过一次:
http://expert.csdn.net/Expert/FAQ/FAQ_Index.asp?id=181014 

 Wolf0403 发表于:2003-10-30 16:01:51
得分:0 

老薛:
我的代码搭配上面的 any_cast 和 main,在 vc7.1 下面工作正常。

老大:
呵呵,应该是所谓 all_cast(boost::any_cast 的名头,俺是不敢动的)
看了你们的那个 cast,就是通过 stringstream 进行的,需要被转换的对象实现了 operator<< 和 operator>>,如果没有就没法用了啊。 

 xueweizhong 发表于:2003-10-30 16:46:43
得分:0 

>to Wolf0403(完美废人) :
是我说得不太清楚,或者是我轻率了,
首先请谅解一下。

我的意思大致是这样用不行:
struct Foo
{
  //...
  template 
  operator T();
  //...
};

int n = any_cast(Foo());
// --> any_cast(Foo())
// --> return_overload
// --> int n = return_overload::operator int
// --> return (int)m_data(m_data is a Foo)
// --> no Foo --> int

但是事实上
int n = Foo();
这一句是可以的,加上any_cast后就不行了。

好象饶了个大圈子。
但应该是个好的反例,是吗?

 xueweizhong 发表于:2003-10-30 17:06:25
得分:0 

>to Wolf0403(完美废人)
仔细看了上面我自己写的
// --> return (int)m_data(m_data is a Foo)
// --> no Foo --> int
应该是可以的。
哎...又犯错了。 

 xueweizhong 发表于:2003-10-31 10:34:17
得分:0 

>to Wolf0403(完美废人) :
有了你这个any_cast模板之后,
我刚开始的贴的那个Library就可以和
return_overload解开耦合关系了:

struct Library
{
  ...
  /* 
  Return_overload get_func(char const* name)
  {
    return GetProcAddress(handle, name);
  }
  */
  // 这一段现在可以改成更自然的方式:
  
  FARPROC get_func(char const* name)
  {
    return GetProcAddress(handle, name);
  }
  ...
};

现在Library类不需要依赖于Return_overload类了。

然后使用的时候需要加一个any_cast:
   // int (*show)(const char*) =
   //                        lib.get_func("xxx.dll");
--->
   int (*show)(const char*) = 
                    any_cast(lib.get_func("xxx.dll"));
                    ^^^^^^^^
这样一个any_cast倒是给人一种倾向性的暗示,可以使代码
更清楚一些。


 Wolf0403 发表于:2003-10-31 18:46:57
得分:0 

老薛:
呵呵,问题解决了?^_^

回头再看看我的代码:
template 
Return_Overload<_Ty> any_cast(const _Ty& _data)
{
return Return_Overload<_Ty>(_data);
}
这里用 _data 和 _Ty 类型构建了一个临时的 Reuturn_Overload 对象并且返回;而返回之后呢,不过是调用了一个
Return_Overload::operator int (*)(const char*) ()...
是不是有点 BT?为什么如果不用这个模板,就一定需要自己写类型转换的信息?而用了 operator _Ty2 就不需要了呢?怪异。。。 

 xueweizhong 发表于:2003-10-31 21:22:47
得分:0 

我想这就是那个所谓的面向对象技术中的公理中的一条吧:
如果不能直接实现,就增加一个中间层。

换句话说:
如果这个功能直接实现,添加一个类(模板or 函数模板)。

any_cast是一个值变换,将一个普通值转换为一个
“值依赖于使用上下文"的值。

“值依赖于使用上下文”这句话更象STL文档中的concept。

呵呵,胡乱说了几句,还是有点不知所云。 

 Wolf0403 发表于:2003-11-01 11:23:14
得分:0 

呵呵,“另外一个间接层”是 《C++ 沉思录》 两个主要思维线索之一(另外一个是:用类表示概念)。这两个用好了,C++ 也就顺手了,呵呵。

我在想的是,为什么 any_cast + Return_Overload 还是不能破解那个 auto_ptr 的 explicit 的构造函数?
auto_ptr p = all_cast(new int(0));
就是
int * -> Return_Overload -> Return_Overload::operator auto_ptr(int *) 嘛,为什么不可以通过呢? 

 xueweizhong 发表于:2003-11-01 20:17:43
得分:0 

>to Wolf0403(完美废人):
我想你所说的auto_ptr的问题应该和下面的更简单的代码等价:

#include 
using namespace std;

struct return_overload
{
  operator auto_ptr ();
};

return_overload source;

//#1: --> 错误
(和auto_ptr target = any_cast(new int)的错误一样,
 与explicit没有关系)
auto_ptr target(source);

//#2: --> 正确
auto_ptr(source)

#1的错误倒是比较好理解,因为auto_ptr的构造函数
auto_ptr(int*)
auto_ptr(auto_ptr&)
auto_ptr(auto_ptr_ref&)
都不可能接受一个r-value的auto_ptr做参数。

可恶的是#2是正确的:
按照我的理解,编译器看到#2这个东东,
考虑的第一件事:
(1)不是
     考虑选择auto_ptr的构造函数
(2)而是
     是否存在return_overload--> auto_ptr的类型转换函数(???)
     于是
     #2等价于
     source.operator auto_ptr()

得到一些结论是:
1 因为C++的构造函数没有名字,
  构造函数调用是从上下文解析出来的,
  这好象可以解释上面两条的一部分原因。

2 return_overload::operator T()
  写成非成员函数为
  T no_name(return_overload*)

  这个no_name你没法直接调用它,
  只能通过指定返回值去调用它:
  return_overload obj;
  obj.operator int();
      ^^^^^^^^^^^^   <------指定返回值

我再想想如何解决any_cast的这个问题,在我们知道
了出错原因后:
  因为auto_ptr的拷贝语义其实是转移语义
  (move-ctor , move-content,...)  

 

 xueweizhong 发表于:2003-11-01 21:59:20
得分:0 

我试了几把,到处是转换,歧义的转换,
感觉上比较恐怖。

或许这是对我们增加了无穷多种转换后的惩罚吧:
template 
operator Target ();

我们可以转换到任何Target!。 

 Wolf0403 发表于:2003-11-01 22:32:11
得分:0 

呵呵。但是必须是有 _Ty2(_Ty1) 的构造函数,或者 _Ty1::operator _Ty2() 的转换函数才可以啊。
我现在想不明白是在哪里出错的。。。VC 也没法跟踪模板参数的类型信息。。。 

 Wolf0403 发表于:2003-11-01 22:36:32
得分:20 

没看到上面一个回复。。。思索ing 

 xueweizhong 发表于:2003-11-02 00:02:56
得分:0 

我现在对auto_ptr的错误理解的更深了,只可惜却
导致要更长篇大论的说。先来点一下:
如果等号,这样:
auto_ptr target = any_cast(new int);
等价于
auto_ptr target = return_overload();
这时编译器去寻找合适的转换,由于
return_overload()可以转换为任何target,我们只考虑
target能接收那些source使
auto_ptr target = source;

因为auto_ptr::auto_ptr(int*) 是explicit的,
所以不再做考虑。
然后还有几个:
    auto_ptr::auto_ptr(auto_ptr&)--#1
    auto_ptr::auto_ptr(auto_ptr_ref); --#2
暂时不考虑这个:
    template 
    auto_ptr::auto_ptr&)--#3

由于这个东西
template 
operator Target ();
并没有对函数signature做什么限制:

#1的情况下:
  Target = auto_ptr&  -->#3
#2的情况下:
  Target = auto_ptr_ref -->#4

这两个都是candidates:
#3情况下:
 return_overload --> auto_ptr& --> auto_ptr
  --------#5
#4情况下:
 return_overload --> auto_ptr_ref  --> auto_ptr
 ---------#6

虽然#5,#6中的第一个转换的函数代码中:
template 
operator Target ()
{
   return (Target)source;----#7
          ^^^^^^^^
}

#7可能不合法,但现在还没有到检查#7是否合法的时间。

由于#5 ,#6是无法比较谁好谁坏的匹配,所以编译器
马上就提示错误了。
而#7的检查要到上面描述的那个最好匹配找到后才进行。







 WindFroce 发表于:2003-11-02 04:39:01
得分:0 

正在学习中 

 xueweizhong 发表于:2003-11-02 17:01:27
得分:0 

由于表达式只考虑l-value, r-value,不考虑引用类型。
所以在我的上贴中的#3:
Target = auto_ptr&  -->#3
是不可能出现的。

这导致
auto_ptr target = any_cast(new int);
在规范的编译器中是合法的。
只可惜我试的那个编译器却提示出错。

现在的结果是:
 return_overload() --> auto_ptr_ref
 --> auto_ptr

而如果写成
auto_ptr target(any_cast(new int))

将导致auto_ptr(int*)也被包括进来,导致
 return_overload() --> int* 
 --> auto_ptr
也加入到这个竞争中,
导致“歧义”错误。


 xueweizhong 发表于:2003-11-02 17:04:56
得分:0 

>to Wolf0403(完美废人):

在一个规范编译器中:
explicit auto_ptr(int*)
^^^^^^^^

中的explicit帮了我们大忙,导致

auto_ptr target = any_cast(new int)

的正确性。

而无论是否有这个explicit

auto_ptr target(any_cast(new int))

都是错误的:有两个歧义的转换等着你。

 plainsong 发表于:2003-11-02 17:43:11
得分:0 

是不是走的太远了?return_overload和any_cast的目标不完全一样,把它们结合在一起是不是有必要?

其实any_cast的的实现应该是这样使用会更自然(更象标准转换):
any_cast(x);
实现为
template
struct any_cast
{
T data;
template 
any_cast(U init_data):data(init_data){}

operator T&(){return data;}
};

也就是说用输出类型实例化类模板,用输入类型实例化成员(上例为构造函数)。

而return_overload正好相反,应该类似于这样:

template
struct return_overload
{
U data;
returl_overload(U init_data):data(init_data){}

template 
operator T (){return T(data);}
};
也就是说,用输入类型实例化类,用输出类型实例化成员,在该成员被实例化时才进行转换操作。

本来any_cast用的转换操作符也是operator T,在面对auto_ptr时不行,我想是因为它要求non-const reference类型而临时对象只能传给const refefence的原因,就改成了operator T&。不过这种方法对于return_overload没有办法使用,因为T对象不是成员而是局部变量,不能返回它的引用,对于函数内部的临时对象是否可以以引用的形式传出,我不太清楚。




 jeckyz 发表于:2003-11-02 18:40:52
得分:0 

脑子有点乱:( 几天后再看 

 xueweizhong 发表于:2003-11-02 18:45:23
得分:0 

不过现在这种实现方式,如果写成这样:
1:
  auto_ptr target = any_cast(new int);
  在auto_ptr_ref的构造函数为
  auto_ptr_ref(int*)
  时是可以的。

2:
  在大多数编译器上auto_ptr_ref的实现为
  auto_ptr_ref(T*)

  但也有VC7.1把它实现为
  auto_ptr_ref(auto_ptr<_Ty>& _Right)
  导致

  template  >
  operator auto_ptr_ref ()
  {
    return (T)data (data is int*)
  }

  这里int*不能转换为auto_ptr_ref。

3 g++的成员模板转换函数的模板参数推导有些
  问题,所以导致这个东东也不能用。


4 所以现在是G++,VC不行,但我试了
  在BCB6上是可以的。
  在VC7.1下把auto_ptr_ref的构造函数修改后也应该
  没有问题。

结论是在合理的条件下使用现在这种实现方式,
可以解决auto_ptr不能转换的问题。
而且是那个explicit auto_ptr(int*)里的explicit帮了忙。


 xueweizhong 发表于:2003-11-02 19:26:17
得分:0 

>to plainsong:
多谢你提醒我,把any_cast和
static_cast, dynamic_cast, reinterpret_cast
划归为一类。

return_overload显然属于另一维。
既然可以用return_overload可以实现
any_cast的不带类型参数的使用方式,
是否可以用return_overload实现相应的
return_staitc_cast,
return_dynamic_cast,
return_interpret_cast使得
功能上和原有的
static_cast, dynamic_cast, interpret_cast相似,而且
不用写类型参数:

struct Father; struct Son : Father {};

Father* father = new Son;
Son* son = return_static_cast(father);
           ^^^^^^^^^^^^^^^^^^
原来是
Son* son = static_cast(father);
这里需要多写一遍Son类型。



 aflyinghorse 发表于:2003-11-11 14:32:45
得分:0 

解释一下前面的一个小问题

struct return_overload
{
  unsigned int data;
  return_overload(unsigned int init_data):data(init_data){};
  template
  operator T() const {std::cout << "operator T()" << std::endl;return T(double(data)/2);}
  operator unsigned int () const {std::cout << "operator unsigned int()" << std::endl;return data >> 1;}
};
return_overload binary(unsigned int data)
{
  return return_overload(data);
}

int main(int argc, char* argv[])
{
double a;
unsigned int b;
a = binary(123);//相当于a = double(123)/2;
b = binary(123);//相当于b = 123 >> 1;
std::cin.get();
return 0;
}

以下的输出结果应该是正确的
operator unsigned int
operator unsigned int

因为编译器在把一个类对象转换为其他类型时会先查找这个类有没有转换
函数,如果没找到才去看是否有成员模板,如果有就把它实例化一个合适
的版本。 所以以上的输出结果没有调用成员模板。 

 xueweizhong 发表于:2003-11-11 16:31:12
得分:0 

>to aflyinghorse()
"因为编译器在把一个类对象转换为其他类型时会先查找这个类有没有转换
函数,如果没找到才去看是否有成员模板,如果有就把它实例化一个合适
的版本。"

描述这个东东的东西在STD的哪个部分出现?
还是....如果STD98中没说的话,很怀疑其正确性...

 xueweizhong 发表于:2003-11-11 16:56:37
得分:0 

如果是的话,还请aflyinghorse多多指教。 

 aflyinghorse 发表于:2003-11-12 00:31:54
得分:0 

xueweizhong(薛卫忠) 
我没有在STD98中找到这样的解释,我是在more effective c++里看到的,
是智能指针那个条款。 

 xueweizhong 发表于:2003-11-12 09:07:30
得分:0 

>to aflyinghorse() 
非常感谢。关于模板方面的事情我一向不太信任Scott Meyer.
所以把more effective c++忽略了,呵呵。

不过我突然又想起些什么,导致我认为你的观点是正确的,
于是我去翻阅了STD98中的
13.3.3.1 Best Viable Function
这一小节。呵呵,有些原理我本是知道的,在碰到实际代码时
却迷糊了一下,下面是说明你那句话的“标准原文”:

Given these defintions, .. viable F1 is .. better 
vialbe F2 ...., and then 
--...
--F1 is non-template function and F2 is a template function specialization
--...

在我们的例子中两种转换都是user-defined conversion,不可比较好坏,
但加上上面这一条后,呵呵,模板被降为“二等公民”了。
$$$@PAGE@###

 xueweizhong 发表于:2003-11-20 21:21:14
得分:0 

又回去看了plainsong的代码变化:

struct return_overload
{
  template
  operator T() const;
  
  operator unsigned int() const;
  // ----------#1
};

----->

struct return_overload
{
private:
  template 
  T do_type_cast(T*) const;

  template <>
  unsigned int do_type_cast(unsigned int *) const;

public:
  template
  operator T() const 
  { 
    return do_type_cast((T*)0);
  }
};

前面那个版本中如果#1是特殊化,将挑战一些编译器的能力。
而不过不是特殊化,而是重载,将导致模板转换函数被永远
屏蔽(具体原因看前面几贴)。

现在我们清楚了这些之后,回过头来看看后一个版本,
呵呵,实现了同样的功能,而且提供一个中间层后,降低了
对编译器的挑战性。

呵呵,果然是个好注意。

 xueweizhong 发表于:2003-11-21 22:53:34
得分:0 

重新提一下原来的东西:

(1)成员模板
     提供了一种类的成员函数的无限可能性
(2)返回值重载
     使得我们的表达式调用语义依赖与使用上下文。

现在我们得到了一些结论:

  1:最多只能提供一个非模板的类型转换函数

  2:如果要使用类型转换函数模板,则
     不能出现非模板的类型转换

  3: 如果要使类型转换函数模板和非模板版本共存,
     可增加一个中间层:
      成员模板函数,通过这个成员模板函数和其
      相应的非模板成员函数重载达到效果。

  4:any_cast可以代替类型的重复书写,含义上更明确些:
     Target target = (Target)source; --->
     Target target = any_cast(source);

  5: auto_ptr是一个坎,编译器的错误描述也比较模糊。
     转换多了的确是会导致增加代码理解难度。

呵呵,期望大家再补充一些.... 

 babysloth 发表于:2003-11-22 16:42:13
得分:0 

多写一个类型不是什么大错……按你这样想,stl里那些functor通通可以把类型参数扔了 

 xueweizhong 发表于:2003-11-22 21:03:34
得分:0 

因为上下文很有限,
所以这里的结论可能回显得粗浅而且不太正确。
比如下面的这个:

 1:最多只能提供一个非模板的类型转换函数

虽然在我们的上下文中:
不允许
  struct Foo 
  {
    operator char () const;
    operator int  () const;
  };
  同时出现,但却允许出现
  struct A;
  struct Foo 
  {
    operator char () const;
    operator A  () const;
  };
  只要这里的类A和char不可能转换到共同的目标。


> babysloth(小懒虫虫) (★★★) 
如果你觉得有些非常不妥的地方,请具体的说明一下。
这样,我会非常感谢。
关于上贴的观点描述,理解上有些模糊,请指教...





 babysloth 发表于:2003-11-23 03:06:20
得分:0 

很久没来了,看到这里讨论满热烈,随便说了两句,见谅。

在一年前的一个project里,我做过一个和你帖子描述的几乎一模一样的东西,一个封装了DLL载入的类,自动转换函数指针类型。自己用起来是很爽,但是同一个team的未必认同。返回值根据类型不同而不同,很容易引起理解上的混乱和实际使用的错误,我更情愿多写一个类型转换:-)

不过这个就扯远了,不好意思。 

 plainsong 发表于:2003-11-23 15:57:02
得分:0 


    想不到连大名鼎鼎的“懒虫”都惊动了……

    后来的讨论我一直没有插嘴,因为感觉把握不住讨论的中心了。我再从一开始的问题开始吧。

主题是“返回值重载”,这是一个很有意思的话题。我们在C++中可以写重载函数,要求不同的重载版本参数列表不同:
  double min(double, double);
  int min(int, int);
但是不允许参数列表相同但返回类型不同的重载:
  double random();
  int random();
这样是不合法的。在Object Pascal中的方法是有一个整数参数的random返回整数(参数是范围),无参数的返回浮点数。

而使用一个“中间对象”的方法其实是可以实现“按返回值重载”的,但事实上重载的不是函数本身,而是类的类型转换操作符。因此,我觉得函数本身也不应该进行计算,只完成对象的构造,把计算改到类型转换操作符中,然后在类定义中为不同类型的转换分别实现不同的操作,这样才能完成重载的真正含义。

歧义当然是可能遇到的,在真正的函数重载机制中同样会遇到歧义,这时我们就要用显式类型转换来解决。只要在“返回值重载”的机制中也能够用显式指定的方法了消除所有歧义,我觉得就已经成功了。 

 babysloth 发表于:2003-11-23 18:12:16
得分:0 

小弟我只是顺便过路啦,呵呵,好久没来了。
这个类似的讨论记得两年前bugn在smiling上有过讨论,
当时似乎是要做一个template化的add,为了返回值折腾了很久,
后来觉得完全是在折腾自己,何苦:-) 

 playmud 发表于:2003-11-23 21:15:24
得分:0 

3颗星的这个说得比较有道理,如果弄出来一个全部泛型的东西,输入返回都不用考虑类型的话,效率肯定会降低,而且容易出现bug,包含的东西越多,考虑不全的地方就越多,其中的转化更是成梯形增长,实用性不会很强,但是从纯粹的技术角度来讲的话,还是比较有积极意义的。以上是一个菜鸟的看法! 

 xueweizhong 发表于:2003-11-24 21:40:00
得分:0 

>to babysloth(小懒虫虫)
既然是路过的前辈高手,也请指点一二。
能提一下你们当时发生的问题吗?
让我们这些后来者也学习一下..... 

 LoveCreatesBeauty 发表于:2003-11-25 16:28:04
得分:0 

学习 

 lyr311 发表于:2003-12-28 20:32:54
得分:0 

这个帖子这么久没人回答啊。
我只想问各位高手一句:
类模板可以成为另外一个类模板的成员,即为成员模板,那么,为什么函数模板不能成为一个类的成员呢??? 

 mechgoukiteng 发表于:2003-12-28 20:58:13
得分:0 

过分追求template技巧不太好吧

感觉上用到ACE那样比较符合工程需要 

 xueweizhong 发表于:2003-12-28 22:15:47
得分:0 

>to  lyr311(老刘:CSDN上瘾只为学习C++!) :
》类模板可以成为另外一个类模板的成员,即为成员模板,
》那么,为什么函数模板不能成为一
》个类的成员呢???

1 C++ 存在函数模板成员

2 不知道你具体想问什么?请说一下。

 lyr311 发表于:2003-12-29 11:44:50
得分:0 

我说的是函数模板,不是模板函数哦?可以吗??? 

 lyr311 发表于:2003-12-29 11:45:34
得分:0 

薛兄可有QQ?很想拜你为师啊??? 

 xueweizhong 发表于:2003-12-29 22:57:25
得分:0 

》to lyr311(老刘:CSDN上瘾只为学习C++!) 
大家共同进步,各有所长,相互学习。 

上一篇:请教C++ primer问题(重载操作符)
下一篇:如何实现C++程序在多系统下运行

分享到: