c++ 模版函数决议

C++语言 码拜 8年前 (2017-05-07) 2450次浏览
决议方法大致如下:
对于模版中不变类型,那么匹配模版声明所在函数
不然匹配实例所在函数
但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;
}
解决方案

1

原因是 vc 编译器是大鳖鳖,高质量 c++ 就忘掉 vc 吧。

1

这跟模板有什么关系?只是一个函数重载的问题。

1

怎么auto都出来了,闻所未闻啊

1

题主你的foo 没有用模版,
只是学名叫做重载函数,实际上是两个不同的函数罢了。

1

函数调用怎么了,就应该是Int型,你本人调用的是结构体里面的函数,返回是int类型的a,你的内联函数是double类型的b,返回的a不是double类型的,为什么要调用内联函数呢?还有这和模板类有什么关系?

2

auto a = ta.geta(); 是应该调用int foo(int) 没问题啊
为什么要调用double的?你这个类模板也是用int作为模板参数
auto b = ta.getb(); 一样调用int的

1

题主你是对的,见temp.nondep
VS不遵循标准不是很正常吗c++ 模版函数决议

30

引用:
Quote: 引用:

原因是 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 会这样,为啥呢,原因是不按照标准走,偷工减料呗,所以本人说它质量差,你有啥不服的?!

1

引用:
Quote: 引用:
Quote: 引用:

原因是 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 会这样,为啥呢,原因是不按照标准走,偷工减料呗,所以本人说它质量差,你有啥不服的?!

非常精彩的回复,不过看来唯一最正确的写法是将全部重载的函数都放在一起,这样就能最优匹配了,不然哪个编译器sb了就烦死了,已经标准也是可以修改的,说不准哪天就改了。

1

引用:
Quote: 引用:
Quote: 引用:

原因是 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 会这样,为啥呢,原因是不按照标准走,偷工减料呗,所以本人说它质量差,你有啥不服的?!

不服的是你随便拍VC,本人学c++完全是从VC开始的,反汇编来直观学习各种c++特性,VS简直是学习c++的利器。
至于你说的这些,本人看不懂,c++标准,不要说英文,就是中文很多都晦涩难懂。
你能扒出这么多标准里专业名词,钻研标准的精神,本人一辈子都干不来。
但你这么随便贬低VS,板砖必须奉上。
刨除你说的那些本人不知道的“标准”,调用int foo(int) {return 1;}  是符合本人的对c++的认知和预期结果的。

1

引用:

不服的是你随便拍VC,本人学c++完全是从VC开始的,反汇编来直观学习各种c++特性,VS简直是学习c++的利器。
至于你说的这些,本人看不懂,c++标准,不要说英文,就是中文很多都晦涩难懂。
你能扒出这么多标准里专业名词,钻研标准的精神,本人一辈子都干不来。
但你这么随便贬低VS,板砖必须奉上。
刨除你说的那些本人不知道的“标准”,调用int foo(int) {return 1;}  是符合本人的对c++的认知和预期结果的。

你有感情关我们什么事。这个点上vc不符合标准就是不符合标准。你看不懂回复,而错误的行为符合你的认知和预期,说明你学习不到位。


CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明c++ 模版函数决议
喜欢 (1)
[1034331897@qq.com]
分享 (0)