BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / soft-design / #32624同步于 2009/2/7
该镜像源已超过 30 天没有更新,可能在源站已被删除。
SoftDesign机器人发帖

【amiao求教】C++中函数模板重载问题[已解决]

flyingmiao
2009/2/7镜像同步7 回复
template<typename T> void f1(unsigned int a, T b) { cout << "1st\n"; }; template<typename T> void f1(T a, T b) { cout << "2rd\n"; }; int main() { f1(1,2); return 0; } 输出: 2rd 为什么? 我个人估计,可能第一个需要转换(int到unsigned int),所以用了第二个。 实际上我注意到,在标准库vector容器中,避免了这个问题。 譬如以下代码 #include <iostream> #include <memory> #include <vector> #include <string> using namespace std; class MyClass { private: MyClass(); public: int val; MyClass(int x = 0):val(x) { cout << "Constructor of " << val << endl << flush; }; }; int main() { vector<MyClass> arr(5, 0); return 0; } vector<MyClass> arr(5, 0); 成功调用了 vector<T>(size_type n, const T &val) 而不是 template<typename In> vector<T>(In begin, In end) (此声明是我个人猜测的) 这里的问题在于,5和0都是int,适用于第一个构造函数 但第二个构造函数模板中,由于begin和end也是同型,可经由模板实参演绎得出,那么此函数模板同样可以演绎出int型的实例,亦即 vector<>(int begin, int end) 换句话说,第二个函数模板亦可适用于代码中调用的情况。 我自己尝试写了一下模拟代码,情况是,由于第一个构造函数需要转换(由int到size_type),调用了第二个构造函数,而导致错误。 也就是没有达到和STL同样的效果 请问,stl库中是如何避免此二义性,并正确调用相应构造函数的?
订阅后,新回复会通过你的通知中心匿名送达。
7 条回复
flyingmiao机器人#1 · 2009/2/7
果然读源码是王道,谜底如下,vs中的stl源码: template<class _Iter> vector(_Iter _First, _Iter _Last) : _Mybase() { // construct from [_First, _Last) _Construct(_First, _Last, _Iter_cat(_First)); } template<class _Iter> void _Construct(_Iter _Count, _Iter _Val, _Int_iterator_tag) { // initialize with _Count * _Val size_type _Size = (size_type)_Count; _Construct_n(_Size, (_Ty)_Val); } template<class _Iter> void _Construct(_Iter _First, _Iter _Last, input_iterator_tag) { // initialize with [_First, _Last), input iterators _Buy(0); _TRY_BEGIN insert(begin(), _First, _Last); _CATCH_ALL _Tidy(); _RERAISE; _CATCH_END } template<class _Iter> inline typename iterator_traits<_Iter>::iterator_category __CLRCALL_OR_CDECL _Iter_cat(const _Iter&) { // return category from iterator argument typename iterator_traits<_Iter>::iterator_category _Cat; return (_Cat); } 通过traits做了个编译期判断(函数重载),也就是说,在STL中, vector<MyClass> arr(5, 0); 调用的实际上是 template<typename In> vector<T>(In begin, In end) 而不是 vector<T>(size_type n, const T &val) 然而vector<MyClass> arr(5, 0);的语义是调用后者,为了达到同样效果 stl封装了一层判断,在 template<class _Iter> vector(_Iter _First, _Iter _Last) 中,通过traits做一个编译期判断(函数重载) 然后分别调用两个_construct函数 正常情况下,会调用 template<class _Iter> void _Construct(_Iter _First,_Iter _Last, input_iterator_tag) 而在vector<MyClass> arr(5, 0);的情况下,会调用 template<class _Iter> void _Construct(_Iter _Count, _Iter _Val, _Int_iterator_tag) 而这个函数的代码,实际上和vector<T>(size_type n, const T &val)是一样的。 NB,我要加精,难得我开个技术贴。
wks机器人#2 · 2009/2/7
nb。崇拜ing
Jarod机器人#3 · 2009/2/7
我想你没有真正明白。。。。。 int main() { f1((unsigned int)1, 2); // uint, int return 0; } 改成这个,输出为 1st 【 在 flyingmiao 的大作中提到: 】 : 果然读源码是王道,谜底如下,vs中的stl源码: : template<class _Iter> : vector(_Iter _First, _Iter _Last) : ...................
Jarod机器人#4 · 2009/2/7
C++ Templates 也说了,在模板arg推演之后,才考虑参数转换。 你贴的VC STL代码里,我怎么没看出来用到traits...........
flyingmiao机器人#5 · 2009/2/7
【 在 Jarod 的大作中提到: 】 : 我想你没有真正明白。。。。。 : int main() { : f1((unsigned int)1, 2); // uint, int : ................... 我知道。。我代码里就这么写的
chopin19机器人#6 · 2009/2/7
怎么觉得不应该拿着两个来比较呢,完全是两个不同的东西 模版是针对的容器内容,而括号里面的并不是容器的内容,和前者函数完全不一样。 函数的那个是函数的形参(括号内的内容)是模版类型。 而在vector<MyClass> arr(5, 0);的情况下,理所应当调用 template<class _Iter> void _Construct(_Iter _Count, _Iter _Val, _Int_iterator_tag) 而不会产生二义性。 而第一个例子(不用vector的)会产生二义性,结果就调用了本不期望调用的 仅此而已。
flyingmiao机器人#7 · 2009/2/7
【 在 chopin19 的大作中提到: 】 : 怎么觉得不应该拿着两个来比较呢,完全是两个不同的东西 : 模版是针对的容器内容,而括号里面的并不是容器的内容,和前者函数完全不一样。 : 函数的那个是函数的形参(括号内的内容)是模版类型。 : ................... vector有这么两个构造函数 1.vector<T>(size_type n, const T &val) MyClass带隐式转换,可由int转换过来,所以这个可行 2.template<typename In> vector<T>(In begin, In end) 这个是成员模板,也可行。可讲In推演为Int,问题是这个构造函数的语义是使用迭代器。 由于重载优先级规则的原因,会调第二个。 但第二个的语义显然与vector<MyClass> arr(5, 2)不符合。 所以只好用上边的方法。在第二个构造函数里判断一下