我对模板上下文中的函数名查找感到困惑。我知道编译器在模板代码中延迟了依赖于参数的标识符查找,直到实例化模板。这意味着您有时可能会出现语法错误或在模板化代码中调用不存在的函数,除非您实际实例化模板,否则编译器不会投诉。模板中的参数相关查找
但是,我发现不同编译器之间存在差异,我很想知道标准本身需要什么。
考虑下面的代码:
#include <iostream>
class Foo
{
public:
template <class T>
void bar(T v)
{
do_something(v);
}
};
void do_something(std::string s)
{
std::cout << "do_something(std::string)" << std::endl;
}
void do_something(int x)
{
std::cout << "do_something(int)" << std::endl;
}
int main()
{
Foo f;
f.bar("abc");
f.bar(123);
}
注意,模板成员函数Foo::bar
调用非参数相关的全局函数称为do_something
,这甚至还没有被宣布呢。
但是,GCC 4.6.3会高兴地编译上述程序。运行时,输出为:
do_something(std::string)
do_something(int)
因此,看起来好像编译器延迟了标识符查找,直到模板实例化,此时它能够找到do_something
。
相比之下,GCC 4.7.2将不是编译上述程序。它产生以下错误:
test.cc: In instantiation of ‘void Foo::bar(T) [with T = const char*]’:
test.cc:27:13: required from here
test.cc:10:3: error: ‘do_something’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
test.cc:19:6: note: ‘void do_something(int)’ declared here, later in the translation unit
所以,GCC 4.7.2意识到do_something
晚宣布,但拒绝编译程序,因为do_something
不是争论有关。
因此,我假设GCC 4.7.2在这里可能是正确的,而GCC 4.6.3是不正确的。所以可能,我需要在Foo::bar
定义之前声明do_something
。这个问题是假设我想允许我的类Foo
的用户通过执行它们自己的do_something
过载来扩展Foo::bar
的行为。我需要写类似:
#include <iostream>
template <class T>
void do_something(T v)
{
std::cout << "do_something(T)" << std::endl;
}
class Foo
{
public:
template <class T>
void bar(T v)
{
do_something(v);
}
};
void do_something(int x)
{
std::cout << "do_something(int)" << std::endl;
}
int main()
{
Foo f;
f.bar("abc");
f.bar(123);
}
这里的问题,是的do_something
重载无法从Foo::bar
中可见,因而不会被调用。所以即使我拨打do_something(int)
,它也会调用do_something(T)
而不是int
的超载。因此,对于GCC 4.6.3和GCC 4.7.2,上述程序输出:
do_something(T)
do_something(T)
那么这里有什么解决方案?我如何允许用户通过实施do_something
自己的过载来扩展Foo::bar
?
甚至引入了“一揽子”与最不理想的转换,问题仍然存在:http://ideone.com/OSGoqJ –