我意识到C ++继承了C的许多要求,其中之一就是编译器不会识别全局函数,除非它以前遇到过这个函数的原型或定义。
这也会影响C ++函数重载。如果函数调用有多个候选者,如果编译器尚未看到它的原型/定义,则“正确”候选者将不会包含在选择过程中。
考虑:
void foo(int v)
{
cout << "Int version" << endl;
}
template <class T>
void call_foo(const T& v)
{
foo(v);
}
void foo(const std::string& v)
{
cout << "Overload for string" << endl;
}
此处,对call_foo(std::string("abc"))
的调用将导致编译器错误,即使foo
的{{1}}超载也是如此。问题是函数模板std::string
是在编译器看到重载之前定义的。
但是,这似乎不适用于全局运算符重载。我们经常重载call_foo
以使我们的自定义类型与C ++ ostreams兼容。无论定义了运算符重载函数,编译器都会选择正确的重载。
例如:
std::ostream& operator << (std::ostream& os, const T&);
在这里,如果我们调用struct Bar { };
template <class T>
void dispatch(const T& v)
{
std::cout << v << std::endl;
}
std::ostream& operator << (std::ostream& os, const Bar& b)
{
os << "Outputting Bar...";
return os;
}
,编译器会调用正确的重载并输出dispatch(Bar())
。
因此,在选择候选函数进行运算符重载时,似乎C ++标准允许更高级的行为。
我的问题是,为什么这种能力不能扩展到常规函数重载?我意识到需要向后兼容C,但这并不会对此产生任何影响,因为只要你编写一个函数重载,你就不会编写一个C程序。
答案 0 :(得分:5)
首先,编译器无需查看定义
实践超载解决;声明就足够了。第二,
问题比你似乎意识到的要复杂得多。在你的功能
模板call_foo
,foo
是一个从属名称,因为它用于
取决于实例化类型的上下文。这意味着它
将被查找两次,一次在模板的位置
定义,第二次实例化模板。这个
然而,第二次查找纯粹是ADL;它不会找到声明
由ADL引进。在您的情况下,您想要的foo
是全局的
命名空间,但在使用std::string
调用它时,唯一的命名空间
ADL考虑的是std::
。
在你的第二个例子中,Bar
在全局命名空间中,所以声明在
将考虑实例化时的全局命名空间。
(请注意,如果dispatch
不是模板,则不会
考虑。)