返回信息流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库中是如何避免此二义性,并正确调用相应构造函数的?
这是一条镜像帖。来源:北邮人论坛 / soft-design / #32624同步于 2009/2/7
该镜像源已超过 30 天没有更新,可能在源站已被删除。
SoftDesign机器人发帖
【amiao求教】C++中函数模板重载问题[已解决]
flyingmiao
2009/2/7镜像同步7 回复
订阅后,新回复会通过你的通知中心匿名送达。
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,我要加精,难得我开个技术贴。
我想你没有真正明白。。。。。
int main() {
f1((unsigned int)1, 2); // uint, int
return 0;
}
改成这个,输出为 1st
【 在 flyingmiao 的大作中提到: 】
: 果然读源码是王道,谜底如下,vs中的stl源码:
: template<class _Iter>
: vector(_Iter _First, _Iter _Last)
: ...................
【 在 Jarod 的大作中提到: 】
: 我想你没有真正明白。。。。。
: int main() {
: f1((unsigned int)1, 2); // uint, int
: ...................
我知道。。我代码里就这么写的
怎么觉得不应该拿着两个来比较呢,完全是两个不同的东西
模版是针对的容器内容,而括号里面的并不是容器的内容,和前者函数完全不一样。
函数的那个是函数的形参(括号内的内容)是模版类型。
而在vector<MyClass> arr(5, 0);的情况下,理所应当调用
template<class _Iter>
void _Construct(_Iter _Count, _Iter _Val, _Int_iterator_tag)
而不会产生二义性。
而第一个例子(不用vector的)会产生二义性,结果就调用了本不期望调用的
仅此而已。
【 在 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)不符合。
所以只好用上边的方法。在第二个构造函数里判断一下