模板的限定名称和非限定名称查找的不同行为
这段代码应该如何表现?如果我在 call_read()
函数中使用限定名称,它会调用通用函数并忽略我的重载;如果我使用非限定名称,它会首先调用重载,然后调用通用版本。有什么区别?这是 GCC 中的错误吗?
#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
How should this code behave? It calls generic function ignoring my overload if I use qualified name in call_read()
function; and it calls overload first and then generic version if I use unqualified name. What's the difference? Is it a bug in GCC?
#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);
}
I also noticed that it works different for fundamental types.
See the code bellow
#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());
}
It is supposed to call overloaded function twice, but it's not.
See the result here
http://codepad.org/iFOOFD52
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您观察到的是两阶段名称查找和参数相关查找的叠加。
让我们看看标准是怎么说的(C++03)。 [温度出发]:
这意味着在
read
和::read
中,read
是一个依赖名称,因为x
是依赖于类型的。这意味着它在实例化时就已解决。让我们看看这个 [temp.dep.candidate] 的规则是什么:因此,对于
::read
情况,仅考虑在模板定义之前声明的函数。但:对于不合格的
read
,这两个函数都会被考虑,这些函数在模板定义和模板实例化时可见。What you're observing is a superposition of two-phase name lookup and argument dependent lookup.
Let's see what the standard says (C++03). [temp.dep]:
That means that in both
read
and::read
,read
is a dependent name becausex
is type-dependent. That means that it's resolved at the point of instantiation. Let's see what are the rules for this [temp.dep.candidate]:Therefore for the
::read
case only functions declared before the template definition are considered. But:for the unqualified
read
both functions are considered, those visible at template definition and template instantiation.是的,这是预期的行为。在第一种情况 (::read) 中,您有效地禁用 ADL(参数相关查找),这将名称查找限制为在使用 read 之前已在全局范围中声明的内容。如果您删除 :: ADL,则可能会解析为您在函数模板之后声明的函数。
编辑:由于对于像
int
和double
这样的基本类型没有 ADL,这解释了你的第二个观察结果。Yes, this is the expected behaviour. In the first case (::read) you effectivly disable ADL (argument dependent lookup) which restricts name lookup to things that have been declared in the global scope before your use of read. If you remove :: ADL will kick which may resolve to functions you declared after your function template.
Edit: And since for fundamental types like
int
anddouble
there is no ADL, this explains your 2nd observation.编译器将始终调用与您的调用最匹配的方法。这里你调用:
因此编译器会更喜欢重载,因为它与调用精确匹配。它在模板专业化的逻辑中,允许说如果存在更适合您的调用的重载函数,那么将使用这个函数。
对于问题的第二部分,关于使用完全限定名称和非限定名称时的差异,它来自以下事实:完全限定名称不依赖于其他任何内容,因此解析为第一个匹配项(此处是您的模板声明)。
The compiler will always call the method which match the most your call. Here you call :
Thus the compiler will prefer the overload as it precisely match the call. It in the logic of template specializations, which permits to say that if an overloaded function exists which better match your call, then this one will be used.
For the second part of the question, concerning the difference when using fully-qualified and unqualified names, it comes from the fact that fully-qualified name is not dependent to anything else, and is thus resolved to the first match (here your template declaration).