C_and_CPP 板


LINE

※ 引述《UNARYvvv (有趣生活)》之铭言: : 推 slchen:谢谢啦~~~... 220.141.56.156 07/29 : 推 UNARYvvv:哈..话说我先前也看不出来为何会错..原来是同名 61.70.137.117 07/29 : → UNARYvvv:不过若是这种情况,不是应该先以引数型别完全相 61.70.137.117 07/29 : → UNARYvvv:同的 non-template function match 到吗?? 61.70.137.117 07/29 : → UNARYvvv:因为照说他们是在同一个 overloaded set 里面吧 61.70.137.117 07/29 : → UNARYvvv:不知道为何反而编译器会选择 Standard 版的呢?? 61.70.137.117 07/29 : 推 otpgoodop:namespace不同.... 59.115.76.234 08/06 : (没想到过了好几天居然又有新推文~) : 的确 namespace 不同没错 : 但不知道这对我的疑问..有解释到什麽呢?? : 请教一下~谢了 U大还记得这个问题,好学精神,令人感佩。 我先重述并稍微简化一下原po的问题: #include <vector> #include <iostream> int distance(std::vector<int> v1, std::vector<int> v2) { return v1.size() - v2.size(); } int main() { std::vector<int> v1(3), v2(1); // 再宣告一次,希望让编译器选用,结果没有 :( int distance(std::vector<int> v1, std::vector<int> v2); int d1 = distance(v1, v2); std::cout << "d1=" << d1 << '\n'; return 0; } 编译错误讯息: /usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/ stl_iterator_base_types.h: In instantiation of `std::iterator_traits<std::vector<int, std::allocator<int> > >': test.cpp:12: instantiated from here /usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/ stl_iterator_base_types.h:129: error: no type named `iterator_category' in `class std::vector<int, std::allocator<int> >' 问题:为什麽它不直接采用我们自定的 global non-template distance() 就好,却和 std::distance() 夹缠不清呢? 我在 comp.lang.c++.moderated 上提了这个问题,结果 引来近五十篇讨论。这个问题颇为复杂,所以我迟迟未来 向大家报告讨论的一些结果,现在既然 U大问起,我试着 整理、说明看看。 真正的问题点并不发生在这个决定要选哪一个函式(也就是 overload resolution)的时间点上,而是在更早之前。也就 是编译过程根本就还没进行到 overload resolution的阶段 就出错了。 首先编译器遇到这个 distance() 函式名称时,就会开始 去一些地方找它的宣告,哪些地方呢?包括 locally visible 的函式宣告,例如在 local block 里宣告的,或是在local block 的任何一个外层宣告而没有被里层盖掉(hide)的, 例如global namespace scope 宣告的函式名字。另外, 因为我们呼叫这个 distance() 时,并没有给它一个 qualifier, 如 ::distance() 或是 std::distance(),这种 unqualified name编译器还会去一个重要的地方寻找它的宣告,也就是 「宣告参数的型别」所在的 namespace 的地方,这就是 所谓的 ADL(Argument Dependent Lookup)又称 Koenig lookup, 这是由 Andrew Koenig 所提出的做法。最初是为了解决 overloaded operator 能够被方便的呼叫使用。这个我就 不再多解释了。 所以除了原po定义的那个 global scope distance() 以外, 编译器还会去找到 std namespace 中的 distance(), 因为 原po在呼叫 distance 时,所给的参数是 vector<int> 而 vector 又是宣告於 std namespace 中,所以被直接或间接 include 进来的 std namespace中所有的函式名称,只要叫 做 distance() 都会被找到。因为找到的 distance() 是个 function template, 所以要先做 template argument deduction 推导成功後,才会将产生的 template instantiation 加到 candidate functions set中,准备进一步做 overload resolution. 现在先将 std::distance() 的宣告式完整的写出来,方便说明。 template<class InputIterator> typename iterator_traits<InputIterator>::difference_type distance(InputIterator first, InputIterator last); 其中的第二行 typename ...... 一整行就是 return type 在做引数推导(template argument deduction)的过程中,first 和 last 这两个参数的 InputIterator 被用 vector<int> 来取代完後, return type 接着也要被取代,才能产生一个「正常的」宣告式,以 便在随後加入 candidate functions set中,於是就变成 iterator_traits<vector<int> >::difference_type 编译器需要确定这个东西到底是不是有效的 type, 如果最终的结果 是无效的 type 那麽 template argument deduction 就失败,这个 函式就不会加入 candidate functions set中,这种「失败」并不会 产生编译错误。 问题是为了确定它有没有效,编译器必须先做一个动作,就是用 vector<int> 来 instantiate iterator_traits<> 这个 class template, 然後再看产生的 class 中有没有宣告 differenct_type 这个型别。 这是 iterator_traits<> 的定义: template<typename _Iterator> struct iterator_traits { typedef typename _Iterator::iterator_category iterator_category; typedef typename _Iterator::value_type value_type; typedef typename _Iterator::difference_type difference_type; typedef typename _Iterator::pointer pointer; typedef typename _Iterator::reference reference; }; 当它用 vector<int> 代入时,就在定义中的第一行发现错误: typedef typename vector<int>::iterator_category iterator_category; vector<int>中并没有宣告 iterator_category,所以编译器就发出编译 错误的讯息。不 instantiate 则已,一做 instantiate, 这种过程中的 错误,编译器都会发出「抱怨」,不让你过。 很有趣的,如果我们偷偷的在 vector<> 中加入一个 iterator_category 的 typedef template</*...*/> vector { public: typedef int iterator_category; // ... }; 让 iterator_traits 的具现化可以顺利完成,而且在紧接着的 distance() 的 return type 的取代动作中,也确实有 difference_type可用 (vector<> 中有 difference_type),於是 std::distance() 会加入 overload functions set,但最後仍然还是原po定义的那个 distance() 会被采用。 甚至於,如果我们除了上述那一个动作以外,还多做了一个动作,就是 偷偷的将上面 iterator_traits 定义中的第三行拿掉: //typedef typename _Iterator::difference_type difference_type; 让 iterator_traits 的具现化可以顺利完成,但是在紧接着的 distance() 的 return type 的取代动作中,找不到 difference_type可用,导致 argument type deduction失败,这种失败,上面说了,不会造成编译错误。 这是所谓的 SFINAE (Substitution Failure Is Not An Error). 以上的说明,但愿能提供不一样的思考方向。 --
QR Code



※ 发信站: 批踢踢实业坊(ptt.cc)
※ 编辑: khoguan 来自: 61.227.252.148 (08/08 15:20)
1F:推 UNARYvvv:真的是太感谢k大了,好丰富的说明啊~ 61.70.137.117 08/08
2F:→ UNARYvvv:本来也是因为我刚好发现有新推文所以才又回的说 61.70.137.117 08/08
3F:推 godfat:头昏,还真复杂 61.224.44.95 08/08
4F:推 khoguan:嗯,和 template 有关的东西的确好复杂滴。 61.227.252.172 08/09







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:iOS站内搜寻

TOP