c++:函数重载和声明顺序的混淆

C++: Confusion over function overloading and order of declaration

本文关键字:顺序 声明 函数 重载 c++      更新时间:2023-10-16

我意识到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"))将导致编译错误,即使std::stringfoo有过载。问题是函数模板call_foo在之前被定义,编译器看到了重载。

然而,这似乎不适用于全局操作符重载。我们定期重载std::ostream& operator << (std::ostream& os, const T&);,使自定义类型与c++ ostreams兼容。不管操作符重载函数在哪里定义,编译器都会选择正确的重载。例如:

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()),编译器调用正确的重载并输出Outputting Bar...

因此,c++标准在选择操作符重载的候选函数时,似乎允许更高级的行为。

我的问题是,为什么这种能力没有扩展到常规的函数重载?我意识到有向后兼容C的要求,但这不会真的有任何影响,因为一旦你写了一个函数重载,你就不是在写一个C程序。

首先,编译器不需要看到定义实践重载解析;一个声明就足够了。第二,问题比你意识到的要复杂得多。在你的函数中模板call_foofoo是一个依赖名称,因为它被用于上下文,它取决于实例化类型。这意味着会被查找两次,一次在模板所在的位置定义,第二次是模板实例化。这然而,第二次查找是纯粹的ADL;它不会发现任何声明由ADL带来的。在您的示例中,您想要的foo在全局中名称空间,但是当使用std::string(唯一的名称空间)调用它时为std:: .

在第二个示例中,Bar位于全局命名空间中,因此在将考虑实例化时的全局名称空间。(注意,如果dispatch不是模板,它就不是模板考虑。)