拍砖没问题,但你得拍到点上才有水平。看你一幅不清楚的样子,给你讲讲吧。
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 会这样,为啥呢,原因是不按照标准走,偷工减料呗,所以本人说它质量差,你有啥不服的?!
1
原因是 vc 编译器是大鳖鳖,高质量 c++ 就忘掉 vc 吧。
这话说的~板砖奉上~
题主你的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 会这样,为啥呢,原因是不按照标准走,偷工减料呗,所以本人说它质量差,你有啥不服的?!
拍砖没问题,但你得拍到点上才有水平。看你一幅不清楚的样子,给你讲讲吧。
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 会这样,为啥呢,原因是不按照标准走,偷工减料呗,所以本人说它质量差,你有啥不服的?!