使用C ++中的模板进行混淆函数查找 [英] Confusing function lookup with templates in C++
问题描述
从以下开始(使用 gcc版本4.0.1
):
namespace name {
template <typename T>
void foo(const T& t) {
bar(t);
}
template <typename T>
void bar(const T& t) {
baz(t);
}
void baz(int) {
std::cout << "baz(int)\n";
}
}
如果我在 / em>命名空间)
If I add (in the global namespace)
struct test {};
void bar(const test&) {
std::cout << "bar(const test&)\n";
}
那么,如我所料,
name::foo(test()); // produces "bar(const test&)"
但如果我只是添加
void bar(const double&) {
std::cout << "bar(const double&)\n";
}
似乎找不到此重载:
name::foo(5.0) // produces "baz(int)"
此外,
typedef std::vector<int> Vec;
void bar(const Vec&) {
std::cout << "bar(const Vec&)\n";
}
也不会出现,因此
name::foo(Vec());
给出编译器错误
error: cannot convert ‘const std::vector<int, std::allocator<int> >’ to ‘int’ for argument ‘1’ to ‘void name::baz(int)’
这是怎么查找应该工作? (注意:如果我删除命名空间 name
,那么一切都按照我的预期工作。)
Is this how the lookup is supposed to work? (Note: if I remove the namespace name
, then everything works as I expected.)
这个例子使得 bar
的任何重载被考虑? (我认为重载应该在之前被考虑在内)
How can I modify this example so that any overload for bar
is considered? (I thought that overloads were supposed to be considered before templates?)
推荐答案
double
版本到全局命名空间,并且在定义所有内容后从main调用 foo
。所以这基本上是两阶段名称查找。查找因为调用中的参数是依赖(取决于其类型)而依赖的不合格函数名称是分两个阶段完成的。
I assume you added the double
version to the global namespace too, and you call foo
from main after everything is defined. So this is basically two phase name lookup. Looking up an unqualified function name that is dependent because an argument in the call is dependent (on its type) is done in two phases.
第一阶段在定义上下文中进行非限定和参数依赖查找。然后它冻结结果,并使用实例化上下文(实例化时的声明的和)仅进行第二参数依赖查找。 无无限制查找已完成。所以对于你的例子,它的意思是:
The first phase does a unqualified and argument dependent lookup in the definition context. It then freezes the result, and using the instantiation context (the sum of the declarations at the point of instantiation) does a second argument dependent lookup only. No unqualified lookup is done anymore. So for your example it means:
-
调用
bar(t)
foo< test>
在实例化上下文中查找bar
使用不合格的查找,因为foo
被声明为上面
栏模板)。根据您是否在foo
模板之前或之后定义全局bar
,它将找到全局<$ c $ (在test
的命名空间中定义)中使用参数依赖查找的c> bar 声明。然后main中的调用将实例化foo< test>
,并且在这个阶段可能找到bar
它在你声明模板后)。
The call
bar(t)
withinfoo<test>
looks upbar
using argument dependent lookup at the instantiation context (it doesn't find it using unqualified lookup, becausefoo
is declaredabove
the bar template). Depending on whether you define the globalbar
before or after thefoo
template, it will find the globalbar
declaration using argument dependent lookup already in the first phase (it's defined intest
's namespace). Then the call in main will instantiatefoo<test>
and it will possible findbar
in this phase (if you declared it after you declared the template).
bar(t) code>不执行参数依赖查找(或者更确切地说,查找的结果是一个空的声明集),因为
模板。调用将是不成形的,标准在 int
是一个基本类型。因此,在定义上下文中的无限定查找也找不到任何东西,因为匹配的 bar
模板被声明为之后
c $ c> foo 14.6.4.2/1
The call bar(t)
within foo<int>
doesn't do argument dependent lookup (or rather, the result for the lookup is an empty declaration set), because int
is a fundamental type. So, unqualified lookup at the definition context will find nothing either, because the matching bar
template is declared after
the foo
template. The call would be ill-formed, and the standard says about this situation at 14.6.4.2/1
如果调用不成功,程序就会有未定义的行为。
If the call would be ill-formed [...] then the program has undefined behavior.
应该考虑这是一个我做了一个脏东西,编译器选择不打我的情况下,我想:)
You should therefor consider this as a "i did a dirty thing and the compiler chose not to slap me" case, i think :)
调用 bar(t)
在内,foo< Vec>
将再次执行查找, std :: (因为那里定义了 std :: vector
)。它没有在定义上下文中找到 bar
。因此,它决定再次通过未定义的行为,并使用 bar
模板,并且其本身也通过使用 baz
在它之后声明,并且无法由定义上下文的ADL或非限定查找找到。
The call bar(t)
within foo<Vec>
will do the lookups again, and will look for bar in std::
(because that's where std::vector
is defined). It doesn't find a bar
there, neither in the definition context. So it decides to go by undefined behavior again, and uses the bar
template, and which in itself again does undefined behavior by using the baz
declared after it and which cannot be found by neither ADL nor unqualified lookup from the definition context.
如果向量是向量< test>
,则查找 bar
也将在全局范围内完成,因为参数依赖查找将不仅直接使用参数类型,而且还在其中包含模板参数的类型(如果有)。
If the vector were a vector<test>
, then lookup for bar
would be done at global scope too, because argument dependent lookup will not only use the argument type directly, but also the type of the template arguments in them, if there are any.
,那么不要完全依赖其行为。在下面的代码中,它声称调用是不明确的,虽然代码是完全正常的 f
在 afake
应该不是候选人。
If you use GCC, then don't rely entirely on its behavior. In the following code, it claims the call is ambiguous, although the code is perfectly fine - the f
in afake
should not be a candidate.
namespace aname {
struct A { };
void f(A) { }
}
namespace afake {
template<typename T>
void g(T t) { f(t); }
void f(aname::A) { }
}
int main() { aname::A a; afake::g(a); }
如果你想测试你的代码片段是否符合,最好使用 gotau在线编译器与严格的设置。
If you want to test your snippets against conformance, best use the comeau online compiler with the strict settings.
这篇关于使用C ++中的模板进行混淆函数查找的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!