此代码应如何运作?如果我在call_read()
函数中使用限定名称,它会调用泛型函数忽略我的重载;如果我使用 unqualified 名称,它首先调用overload,然后调用泛型版本。有什么不同?这是海湾合作委员会的一个错误吗?
#include <iostream>
struct info1 {};
struct info2 {};
template<class T> void read(T& x)
{
std::cout << "generic" << std::endl;
}
template<class T> void call_read(T& x)
{
::read(x); // if I replace ::read(x) with read(x) the overload is called
}
void read(info1& x)
{
std::cout << "overload" << std::endl;
}
int main()
{
info1 x;
info2 y;
call_read(x);
call_read(y);
}
我也注意到它对基本类型有所不同。 请参阅下面的代码
#include <iostream>
typedef struct info1 {};
typedef struct info2 {};
typedef int info3;
typedef double info4;
template<class T> void read(T x)
{
std::cout << "generic" << std::endl;
}
template<class T> void call_read(T x)
{
read(x);
}
void read(info1 x)
{
std::cout << "overload" << std::endl;
}
void read(info3 x)
{
std::cout << "overload" << std::endl;
}
int main()
{
call_read(info1());
call_read(info2());
call_read(info3());
call_read(info4());
}
应该两次调用重载函数,但事实并非如此。 在这里查看结果 http://codepad.org/iFOOFD52
答案 0 :(得分:9)
您所观察到的是两阶段名称查找和参数相关查找的叠加。
让我们看看标准是什么(C ++ 03)。 [temp.dep]:
[...]表达形式:
postfix-expression ( expression-listopt )
其中postfix-expression是标识符,当且仅当表达式列表中的任何表达式是依赖于类型的表达式时,标识符表示从属名称(14.6.2.2)。
这意味着在read
和::read
中,read
是一个从属名称,因为x
与类型有关。这意味着它在实例化时得到了解决。让我们看看这个[temp.dep.candidate]的规则是什么:
对于依赖于模板参数的函数调用,如果函数名称是unqualified-id但不是template-id,则使用通常的查找规则(3.4.1,3.4.2)找到候选函数,除了的是:
- 对于使用非限定名称查找(3.4.1)的查找部分,只能找到模板定义上下文中带有外部链接的函数声明。
因此,对于::read
情况,仅考虑在模板定义之前声明的函数。但是:
- 对于使用关联命名空间(3.4.2)进行查找的部分,仅在模板定义上下文或模板<中找到具有外部链接的函数声明找到strong>实例化上下文。
对于非限定read
这两个函数都被考虑,在模板定义和模板实例化时可见。
答案 1 :(得分:5)
是的,这是预期的行为。在第一种情况下(:: read),您有效地禁用ADL(依赖于参数的查找),这会将名称查找限制为在使用read之前已在全局范围中声明的内容。如果删除:: ADL将踢出可能会解析为在功能模板之后声明的函数。
编辑:因为对于int
和double
等基本类型没有ADL,这就解释了你的第二次观察。
答案 2 :(得分:-1)
编译器将始终调用与您的调用最匹配的方法。在这里你打电话:
read(T& x) [with T = info1]
因此,编译器会优先选择重载,因为它与调用完全匹配。它在模板特化的逻辑中,允许说如果存在一个更符合你的调用的重载函数,那么将使用这个。
对于问题的第二部分,关于使用完全限定名称和非限定名称的区别,它来自完全限定名称不依赖于任何其他内容的事实,因此解决了第一个匹配(这里你的模板声明)。