从以下开始(使用gcc version 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";
}
}
如果我添加(在全局命名空间中)
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
,那么一切都按预期工作。)
如何修改此示例以便考虑bar
的任何重载? (我认为重载应该在模板之前被认为是?)
答案 0 :(得分:8)
我假设您也将double
版本添加到全局命名空间,并在定义完所有内容后从main调用foo
。所以这基本上是两阶段名称查找。查找依赖的非限定函数名,因为调用中的参数依赖于(在其类型上)两个阶段。
第一阶段在定义上下文中执行非限定和参数依赖的查找。然后它冻结结果,并使用实例化上下文(实例化时声明的总和)执行第二个参数依赖查找仅。 否不再进行非限定查询。因此,对于您的示例,它意味着:
bar(t)
内的调用foo<test>
在实例化上下文中使用参数相关查找查找bar
(它使用非限定查找找不到它,因为{{1} }将foo
声明为条形模板。根据您是在above
模板之前还是之后定义全局bar
,它将在第一阶段中找到使用参数相关查找的全局foo
声明(它在{{1中定义)命名空间)。然后main中的调用将实例化bar
,并且在此阶段可能会找到test
(如果您在声明模板后声明了它)。
foo<test>
内的调用bar
不执行与参数相关的查找(或者更确切地说,查找的结果是空的声明集),因为bar(t)
是一个基本类型。因此,定义上下文中的非限定查找也不会发现任何内容,因为匹配的foo<int>
模板被声明为int
bar
模板。电话会议形成不良,标准会在after
如果呼叫形成不良[...]则程序会有不确定的行为。
你应该认为这是“我做了一件肮脏的事情而编译器选择不打我”的情况,我认为:)
foo
内的来电14.6.4.2/1
会再次进行查询,并会在bar(t)
中查找条形码(因为这是定义foo<Vec>
的地方)。它在定义上下文中都没有找到std::
。所以它决定再次使用未定义的行为,并使用std::vector
模板,它本身再次通过使用后面声明的bar
来执行未定义的行为,并且ADL和非限定查找都找不到它从定义上下文。
如果向量是bar
,那么baz
的查找也将在全局范围内完成,因为依赖于参数的查找不仅会直接使用参数类型,还会使用模板的类型它们中的参数,如果有的话。
如果你使用GCC,那么就不要完全依赖它的行为。在下面的代码中,它声称调用是不明确的,尽管代码完全正常 - vector<test>
中的bar
不应该是候选者。
f
如果您想针对一致性测试您的代码段,请最好使用严格设置的comeau online compiler。
答案 1 :(得分:2)
我可以确认您在我的系统上看到的行为,我认为这是正确的。
看起来重载解析只是查看它的参数的名称空间,因此bar
的版本使test
起作用,因为test
在全局命名空间中,因此编译器在那里检查bar
的版本,正如你正确指出的那样,它优先于模板版本。
对于Vec
版本,重要命名空间为std
。如果您在bar
中放置了std
版本,则会发现它会将其选中。
double
版本不起作用,因为全局命名空间不用于查找,因为double
是内置类型,并且不以任何方式与全局命名空间特别关联。
答案 2 :(得分:2)
在“c ++ koenig lookup”
上做谷歌这应该为您提供有关模板查找规则的足够信息。
Herb Sutter有一篇关于这个主题的好文章:
http://www.gotw.ca/gotw/030.htm
答案 3 :(得分:0)
查找名称的规则是,如果名称不合格,则参数的命名空间将用于搜索该函数。
name::foo(test());
有效,因为在foo
中您基本上调用了bar(test());
,并且测试的命名空间用于搜索栏。在这种情况下,全局命名空间。
name::foo(Vec());
这不会起作用,因为Vec是一个typedef而不是类或结构。
请参阅函数名称查找规则的C ++标准。
答案 4 :(得分:-1)
以下程序适用于gcc 4.3和gcc 4.1(我手边只有两个编译器:
#include <iostream>
#include <vector>
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";
}
struct test {};
void bar(const test&) {
std::cout << "bar(const test&)\n";
}
void bar(const double&) {
std::cout << "bar(const double&)\n";
}
typedef std::vector<int> Vec;
void bar(const Vec&) {
std::cout << "bar(const Vec&)\n";
}
}
int main()
{
name::foo(name::test());
name::foo(5.0);
name::foo(name::Vec());
}
产:
bar(const test&amp;)
bar(const double&amp;)
bar(const Vec&amp;)
您使用的是哪种编译器?
答案 5 :(得分:-1)
以下代码使用VS 2005 Professional Edition编译好:
#include <iostream>
#include <vector>
using std::cout;
typedef std::vector<int> Vec;
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";
}
void bar(const Vec&) {
std::cout << "bar(const Vec&)\n";
}
}
int main()
{
name::foo(Vec());
return 0;
}
请发布原始代码,以便我们找出错误。