c++ 模版函数决议

GKatHere 2017-03-31 12:41:04
决议方法大概如下:
对于模版中不变类型,那么匹配模版声明所在函数
不然匹配实例所在函数
但VS中似乎并不如此:

//XXX.h
inline
double foo(double) {return 10;};

template<class TP_>
struct TA
{
int a;
TP_ b;
int geta() {return foo(a);}
TP_ getb() {return foo(b);}
};

//main cpp
int foo(int) {return 1;};

int _tmain(int argc, _TCHAR* argv[])
{
TA<int> ta;
auto a = ta.geta(); // foo应该调用double foo(double) , 但调用的是int foo(int)
auto b = ta.getb();
getchar();
return 0;
}

...全文
215 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
ri_aje 2017-04-01
  • 打赏
  • 举报
回复
引用 4 楼 mymixing 的回复:
[quote=引用 1 楼 ri_aje 的回复:] 因为 vc 编译器是大鳖鳖,高质量 c++ 就忘掉 vc 吧。
这话说的~板砖奉上~[/quote]
引用 5 楼 mymixing 的回复:
楼主你的foo 没有用模版, 只是学名叫做重载函数,实际上是两个不同的函数罢了。
拍砖没问题,但你得拍到点上才有水平。看你一幅不明白的样子,给你讲讲吧。 geta/b 里对 foo 的调用,首先要解决 name lookup 的问题,即这个 foo 到底能够表示那些函数,完成这步以后,才是重载解析呢,即在所有可选的函数里,选一个最好的。在主楼的程序里,当编译 geta/b 的时候,编译器只见过 foo(double),后面虽然有 foo(int),但是在成员函数定义处还不可见呢。 然后编译器需要决定是否现在就把 foo 定死为 foo(double) 并且结束 name lookup,这在编译普通类成员的时候,都是直接就定了。惟独编译模版的时候,c++ 有一个法则叫 two phase name lookup,说的是对 geta/b 中涉及到的 name 可能有两个决议点,一个点叫 point of definition,一个点叫 point of instantiation。在主楼的写法中,point of definition 决议的 foo 只有 foo(double),而 point of instantiation 决议的 foo 则有 foo(double) 和 foo(int),因为在后面 auto b = ta.getb(); 的时候,foo(int) 是可见的。如果采用后一种方法,那么经重载解析都会得出 getb 调用 foo(int) 的结论。不过这里有个关键点,就是 c++ 只允许对 dependent name 采用 two phase name lookup,而 geta/b 中的 foo 都不是 dependent name,所以根据语言规范,它们必须在 geta/b 定义处就完成决议,而此时可见的只有 foo(double),重载解析进来以后看到的重载候选集中压根就没有 foo(int),自然也不可能生成对它的调用了。 g++ 和 clang 编译主楼程序都能输出,10, 10。vc 的错误输出显然是采用了 two phase name lookup 造成的,而这是不正确滴。实际上 vc 编译器根本就没有实现 two phase lookup,为了最大限度掩盖这一点带来的问题,所有名字都是按照 point of instantiation 来决议的,即不加分别的都采用 two phase lookup,所以在 vc 看来,foo(int) 是可见的。而楼主问的就是为啥 vc 会这样,为啥呢,因为不按照标准走,偷工减料呗,所以我说它质量差,你有啥不服的?!
ri_aje 2017-04-01
  • 打赏
  • 举报
回复
引用 11 楼 mymixing 的回复:
[quote=引用 9 楼 ri_aje 的回复:] [quote=引用 4 楼 mymixing 的回复:] [quote=引用 1 楼 ri_aje 的回复:] 因为 vc 编译器是大鳖鳖,高质量 c++ 就忘掉 vc 吧。
这话说的~板砖奉上~[/quote]
引用 5 楼 mymixing 的回复:
楼主你的foo 没有用模版, 只是学名叫做重载函数,实际上是两个不同的函数罢了。
拍砖没问题,但你得拍到点上才有水平。看你一幅不明白的样子,给你讲讲吧。 geta/b 里对 foo 的调用,首先要解决 name lookup 的问题,即这个 foo 到底能够表示那些函数,完成这步以后,才是重载解析呢,即在所有可选的函数里,选一个最好的。在主楼的程序里,当编译 geta/b 的时候,编译器只见过 foo(double),后面虽然有 foo(int),但是在成员函数定义处还不可见呢。 然后编译器需要决定是否现在就把 foo 定死为 foo(double) 并且结束 name lookup,这在编译普通类成员的时候,都是直接就定了。惟独编译模版的时候,c++ 有一个法则叫 two phase name lookup,说的是对 geta/b 中涉及到的 name 可能有两个决议点,一个点叫 point of definition,一个点叫 point of instantiation。在主楼的写法中,point of definition 决议的 foo 只有 foo(double),而 point of instantiation 决议的 foo 则有 foo(double) 和 foo(int),因为在后面 auto b = ta.getb(); 的时候,foo(int) 是可见的。如果采用后一种方法,那么经重载解析都会得出 getb 调用 foo(int) 的结论。不过这里有个关键点,就是 c++ 只允许对 dependent name 采用 two phase name lookup,而 geta/b 中的 foo 都不是 dependent name,所以根据语言规范,它们必须在 geta/b 定义处就完成决议,而此时可见的只有 foo(double),重载解析进来以后看到的重载候选集中压根就没有 foo(int),自然也不可能生成对它的调用了。 g++ 和 clang 编译主楼程序都能输出,10, 10。vc 的错误输出显然是采用了 two phase name lookup 造成的,而这是不正确滴。实际上 vc 编译器根本就没有实现 two phase lookup,为了最大限度掩盖这一点带来的问题,所有名字都是按照 point of instantiation 来决议的,即不加分别的都采用 two phase lookup,所以在 vc 看来,foo(int) 是可见的。而楼主问的就是为啥 vc 会这样,为啥呢,因为不按照标准走,偷工减料呗,所以我说它质量差,你有啥不服的?![/quote] 不服的是你随便拍VC,我学c++完全是从VC开始的,反汇编来直观学习各种c++特性,VS简直是学习c++的利器。 至于你说的这些,我看不懂,c++标准,不要说英文,就是中文很多都晦涩难懂。 你能扒出这么多标准里专业名词,钻研标准的精神,我一辈子都干不来。 但你这么随便贬低VS,板砖必须奉上。 刨除你说的那些我不知道的“标准”,调用int foo(int) {return 1;} 是符合我的对c++的认知和预期结果的。 [/quote] 童鞋,客观的说我没有随便拍 VC,我拍是基于事实的,倒是你这块板砖拍的有些随心所欲。VS IDE 做的好我同意,但这和主楼的问题木有关系,你想夸 VC,也得换个合适的地方。比如我曾经小白的时候,读谭浩强 C 语言觉得受益匪浅呢,但不能因为这个,别人有具体理由吐槽谭浩强的时候我就要跳出来拍板砖,对吧。主楼展现的问题 (vc 没有正确实现 two phase lookup) 可不是一个可有可无的小问题,这是 c++ 非常基础的一个概念,直接关系到很多程序的正确性,vc 存在了20多年,连这都没搞对,还不许吐个槽吗。最后奉劝你按照正确的方法学习 c++,先研读标准,再细致入微的跟汇编,否则容易出现错误的幻觉,比如 "调用int foo(int) {return 1;} 是符合我的对c++的认知和预期结果的"。
GKatHere 2017-04-01
  • 打赏
  • 举报
回复
引用 9 楼 ri_aje 的回复:
[quote=引用 4 楼 mymixing 的回复:] [quote=引用 1 楼 ri_aje 的回复:] 因为 vc 编译器是大鳖鳖,高质量 c++ 就忘掉 vc 吧。
这话说的~板砖奉上~[/quote]
引用 5 楼 mymixing 的回复:
楼主你的foo 没有用模版, 只是学名叫做重载函数,实际上是两个不同的函数罢了。
拍砖没问题,但你得拍到点上才有水平。看你一幅不明白的样子,给你讲讲吧。 geta/b 里对 foo 的调用,首先要解决 name lookup 的问题,即这个 foo 到底能够表示那些函数,完成这步以后,才是重载解析呢,即在所有可选的函数里,选一个最好的。在主楼的程序里,当编译 geta/b 的时候,编译器只见过 foo(double),后面虽然有 foo(int),但是在成员函数定义处还不可见呢。 然后编译器需要决定是否现在就把 foo 定死为 foo(double) 并且结束 name lookup,这在编译普通类成员的时候,都是直接就定了。惟独编译模版的时候,c++ 有一个法则叫 two phase name lookup,说的是对 geta/b 中涉及到的 name 可能有两个决议点,一个点叫 point of definition,一个点叫 point of instantiation。在主楼的写法中,point of definition 决议的 foo 只有 foo(double),而 point of instantiation 决议的 foo 则有 foo(double) 和 foo(int),因为在后面 auto b = ta.getb(); 的时候,foo(int) 是可见的。如果采用后一种方法,那么经重载解析都会得出 getb 调用 foo(int) 的结论。不过这里有个关键点,就是 c++ 只允许对 dependent name 采用 two phase name lookup,而 geta/b 中的 foo 都不是 dependent name,所以根据语言规范,它们必须在 geta/b 定义处就完成决议,而此时可见的只有 foo(double),重载解析进来以后看到的重载候选集中压根就没有 foo(int),自然也不可能生成对它的调用了。 g++ 和 clang 编译主楼程序都能输出,10, 10。vc 的错误输出显然是采用了 two phase name lookup 造成的,而这是不正确滴。实际上 vc 编译器根本就没有实现 two phase lookup,为了最大限度掩盖这一点带来的问题,所有名字都是按照 point of instantiation 来决议的,即不加分别的都采用 two phase lookup,所以在 vc 看来,foo(int) 是可见的。而楼主问的就是为啥 vc 会这样,为啥呢,因为不按照标准走,偷工减料呗,所以我说它质量差,你有啥不服的?![/quote]赞一个
FancyMouse 2017-04-01
  • 打赏
  • 举报
回复
引用 11 楼 mymixing 的回复:
不服的是你随便拍VC,我学c++完全是从VC开始的,反汇编来直观学习各种c++特性,VS简直是学习c++的利器。 至于你说的这些,我看不懂,c++标准,不要说英文,就是中文很多都晦涩难懂。 你能扒出这么多标准里专业名词,钻研标准的精神,我一辈子都干不来。 但你这么随便贬低VS,板砖必须奉上。 刨除你说的那些我不知道的“标准”,调用int foo(int) {return 1;} 是符合我的对c++的认知和预期结果的。
你有感情关我们什么事。这个点上vc不符合标准就是不符合标准。你看不懂回复,而错误的行为符合你的认知和预期,说明你学习不到位。
Enter空格 2017-04-01
  • 打赏
  • 举报
回复
引用 9 楼 ri_aje 的回复:
[quote=引用 4 楼 mymixing 的回复:] [quote=引用 1 楼 ri_aje 的回复:] 因为 vc 编译器是大鳖鳖,高质量 c++ 就忘掉 vc 吧。
这话说的~板砖奉上~[/quote]
引用 5 楼 mymixing 的回复:
楼主你的foo 没有用模版, 只是学名叫做重载函数,实际上是两个不同的函数罢了。
拍砖没问题,但你得拍到点上才有水平。看你一幅不明白的样子,给你讲讲吧。 geta/b 里对 foo 的调用,首先要解决 name lookup 的问题,即这个 foo 到底能够表示那些函数,完成这步以后,才是重载解析呢,即在所有可选的函数里,选一个最好的。在主楼的程序里,当编译 geta/b 的时候,编译器只见过 foo(double),后面虽然有 foo(int),但是在成员函数定义处还不可见呢。 然后编译器需要决定是否现在就把 foo 定死为 foo(double) 并且结束 name lookup,这在编译普通类成员的时候,都是直接就定了。惟独编译模版的时候,c++ 有一个法则叫 two phase name lookup,说的是对 geta/b 中涉及到的 name 可能有两个决议点,一个点叫 point of definition,一个点叫 point of instantiation。在主楼的写法中,point of definition 决议的 foo 只有 foo(double),而 point of instantiation 决议的 foo 则有 foo(double) 和 foo(int),因为在后面 auto b = ta.getb(); 的时候,foo(int) 是可见的。如果采用后一种方法,那么经重载解析都会得出 getb 调用 foo(int) 的结论。不过这里有个关键点,就是 c++ 只允许对 dependent name 采用 two phase name lookup,而 geta/b 中的 foo 都不是 dependent name,所以根据语言规范,它们必须在 geta/b 定义处就完成决议,而此时可见的只有 foo(double),重载解析进来以后看到的重载候选集中压根就没有 foo(int),自然也不可能生成对它的调用了。 g++ 和 clang 编译主楼程序都能输出,10, 10。vc 的错误输出显然是采用了 two phase name lookup 造成的,而这是不正确滴。实际上 vc 编译器根本就没有实现 two phase lookup,为了最大限度掩盖这一点带来的问题,所有名字都是按照 point of instantiation 来决议的,即不加分别的都采用 two phase lookup,所以在 vc 看来,foo(int) 是可见的。而楼主问的就是为啥 vc 会这样,为啥呢,因为不按照标准走,偷工减料呗,所以我说它质量差,你有啥不服的?![/quote] 不服的是你随便拍VC,我学c++完全是从VC开始的,反汇编来直观学习各种c++特性,VS简直是学习c++的利器。 至于你说的这些,我看不懂,c++标准,不要说英文,就是中文很多都晦涩难懂。 你能扒出这么多标准里专业名词,钻研标准的精神,我一辈子都干不来。 但你这么随便贬低VS,板砖必须奉上。 刨除你说的那些我不知道的“标准”,调用int foo(int) {return 1;} 是符合我的对c++的认知和预期结果的。
bravery36 2017-04-01
  • 打赏
  • 举报
回复
引用 9 楼 ri_aje 的回复:
[quote=引用 4 楼 mymixing 的回复:] [quote=引用 1 楼 ri_aje 的回复:] 因为 vc 编译器是大鳖鳖,高质量 c++ 就忘掉 vc 吧。
这话说的~板砖奉上~[/quote]
引用 5 楼 mymixing 的回复:
楼主你的foo 没有用模版, 只是学名叫做重载函数,实际上是两个不同的函数罢了。
拍砖没问题,但你得拍到点上才有水平。看你一幅不明白的样子,给你讲讲吧。 geta/b 里对 foo 的调用,首先要解决 name lookup 的问题,即这个 foo 到底能够表示那些函数,完成这步以后,才是重载解析呢,即在所有可选的函数里,选一个最好的。在主楼的程序里,当编译 geta/b 的时候,编译器只见过 foo(double),后面虽然有 foo(int),但是在成员函数定义处还不可见呢。 然后编译器需要决定是否现在就把 foo 定死为 foo(double) 并且结束 name lookup,这在编译普通类成员的时候,都是直接就定了。惟独编译模版的时候,c++ 有一个法则叫 two phase name lookup,说的是对 geta/b 中涉及到的 name 可能有两个决议点,一个点叫 point of definition,一个点叫 point of instantiation。在主楼的写法中,point of definition 决议的 foo 只有 foo(double),而 point of instantiation 决议的 foo 则有 foo(double) 和 foo(int),因为在后面 auto b = ta.getb(); 的时候,foo(int) 是可见的。如果采用后一种方法,那么经重载解析都会得出 getb 调用 foo(int) 的结论。不过这里有个关键点,就是 c++ 只允许对 dependent name 采用 two phase name lookup,而 geta/b 中的 foo 都不是 dependent name,所以根据语言规范,它们必须在 geta/b 定义处就完成决议,而此时可见的只有 foo(double),重载解析进来以后看到的重载候选集中压根就没有 foo(int),自然也不可能生成对它的调用了。 g++ 和 clang 编译主楼程序都能输出,10, 10。vc 的错误输出显然是采用了 two phase name lookup 造成的,而这是不正确滴。实际上 vc 编译器根本就没有实现 two phase lookup,为了最大限度掩盖这一点带来的问题,所有名字都是按照 point of instantiation 来决议的,即不加分别的都采用 two phase lookup,所以在 vc 看来,foo(int) 是可见的。而楼主问的就是为啥 vc 会这样,为啥呢,因为不按照标准走,偷工减料呗,所以我说它质量差,你有啥不服的?![/quote] 非常精彩的回复,不过看来唯一最正确的写法是将所有重载的函数都放在一起,这样就能最优匹配了,不然哪个编译器sb了就烦死了,已经标准也是可以修改的,说不准哪天就改了。
  • 打赏
  • 举报
回复
原来还有坑啊,学习了。 我试了一下,Borland C++(bcc32 6.70)、ms VC++(cl 19.00.24210 32/64位)、intel C++(icl 15.0.2.179 32/64位)在这个“特性”上的举止是一样的,而gcc 5.1、clang/llvm 3.1则符合“标准”,不过C++Builder 的64位编译器 bcc64 6.70是基于clang/llvm的修改版,与后两者一致。看来这一“特性”可以区分两大阵营。
paschen 版主 2017-04-01
  • 打赏
  • 举报
回复
引用 9 楼 ri_aje 的回复:
[quote=引用 4 楼 mymixing 的回复:] [quote=引用 1 楼 ri_aje 的回复:] 因为 vc 编译器是大鳖鳖,高质量 c++ 就忘掉 vc 吧。
这话说的~板砖奉上~[/quote]
引用 5 楼 mymixing 的回复:
楼主你的foo 没有用模版, 只是学名叫做重载函数,实际上是两个不同的函数罢了。
拍砖没问题,但你得拍到点上才有水平。看你一幅不明白的样子,给你讲讲吧。 geta/b 里对 foo 的调用,首先要解决 name lookup 的问题,即这个 foo 到底能够表示那些函数,完成这步以后,才是重载解析呢,即在所有可选的函数里,选一个最好的。在主楼的程序里,当编译 geta/b 的时候,编译器只见过 foo(double),后面虽然有 foo(int),但是在成员函数定义处还不可见呢。 然后编译器需要决定是否现在就把 foo 定死为 foo(double) 并且结束 name lookup,这在编译普通类成员的时候,都是直接就定了。惟独编译模版的时候,c++ 有一个法则叫 two phase name lookup,说的是对 geta/b 中涉及到的 name 可能有两个决议点,一个点叫 point of definition,一个点叫 point of instantiation。在主楼的写法中,point of definition 决议的 foo 只有 foo(double),而 point of instantiation 决议的 foo 则有 foo(double) 和 foo(int),因为在后面 auto b = ta.getb(); 的时候,foo(int) 是可见的。如果采用后一种方法,那么经重载解析都会得出 getb 调用 foo(int) 的结论。不过这里有个关键点,就是 c++ 只允许对 dependent name 采用 two phase name lookup,而 geta/b 中的 foo 都不是 dependent name,所以根据语言规范,它们必须在 geta/b 定义处就完成决议,而此时可见的只有 foo(double),重载解析进来以后看到的重载候选集中压根就没有 foo(int),自然也不可能生成对它的调用了。 g++ 和 clang 编译主楼程序都能输出,10, 10。vc 的错误输出显然是采用了 two phase name lookup 造成的,而这是不正确滴。实际上 vc 编译器根本就没有实现 two phase lookup,为了最大限度掩盖这一点带来的问题,所有名字都是按照 point of instantiation 来决议的,即不加分别的都采用 two phase lookup,所以在 vc 看来,foo(int) 是可见的。而楼主问的就是为啥 vc 会这样,为啥呢,因为不按照标准走,偷工减料呗,所以我说它质量差,你有啥不服的?![/quote]
  • 打赏
  • 举报
回复
这跟模板有什么关系?只是一个函数重载的问题。
ri_aje 2017-03-31
  • 打赏
  • 举报
回复
因为 vc 编译器是大鳖鳖,高质量 c++ 就忘掉 vc 吧。
xskxzr 2017-03-31
  • 打赏
  • 举报
回复
楼主你是对的,见temp.nondep VS不遵循标准不是很正常吗
paschen 版主 2017-03-31
  • 打赏
  • 举报
回复
auto a = ta.geta(); 是应该调用int foo(int) 没问题啊 为什么要调用double的?你这个类模板也是用int作为模板参数 auto b = ta.getb(); 一样调用int的
qq_33866143 2017-03-31
  • 打赏
  • 举报
回复
函数调用怎么了,就应该是Int型,你自己调用的是结构体里面的函数,返回是int类型的a,你的内联函数是double类型的b,返回的a不是double类型的,为什么要调用内联函数呢?还有这和模板类有什么关系?
Enter空格 2017-03-31
  • 打赏
  • 举报
回复
楼主你的foo 没有用模版, 只是学名叫做重载函数,实际上是两个不同的函数罢了。
Enter空格 2017-03-31
  • 打赏
  • 举报
回复
引用 1 楼 ri_aje 的回复:
因为 vc 编译器是大鳖鳖,高质量 c++ 就忘掉 vc 吧。
这话说的~板砖奉上~
imarshal 2017-03-31
  • 打赏
  • 举报
回复
怎么auto都出来了,闻所未闻啊

65,208

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧