我对模板上下文中的函数名查找感到困惑。我知道编译器会延迟模板化代码中与参数相关的标识符查找,直到模板被实例化。这意味着您有时可能会遇到语法错误或在模板代码中调用不存在的函数,除非您实际实例化模板,否则编译器不会抱怨。
但是,我发现不同编译器之间存在差异,我有兴趣了解标准本身的要求。
考虑以下代码:
#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 是不正确的。所以大概,我需要在定义do_something
之前声明。Foo::bar
这个问题是假设我想允许我的类的用户通过实现他们自己的重载Foo
来扩展. 我需要写一些类似的东西:Foo::bar
do_something
#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)
而不是重载 for int
。因此,对于 GCC 4.6.3 和 GCC 4.7.2,上述程序输出:
do_something(T)
do_something(T)
那么这里有哪些解决方案呢?如何允许用户Foo::bar
通过实现自己的重载来扩展do_something
?