【发布时间】:2011-07-27 18:25:21
【问题描述】:
我意识到 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::string 的foo 过载。问题是函数模板call_foo 是在编译器看到重载之前定义的。
但是,这似乎不适用于全局运算符重载。我们定期重载 std::ostream& operator << (std::ostream& os, const T&); 以使我们的自定义类型与 C++ ostream 兼容。编译器会选择正确的重载,而不管运算符重载函数是在哪里定义的。
例如:
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 程序了。
【问题讨论】:
-
第二个
foo不是特化,它只是另一个重载名称 foo 的函数。 -
我不明白您的
operator<<示例,因为它与您的第一个示例无法比较。没有调度模板,只有各种重载。 -
@Gman,关键是如果调度模板被定义之前写入
operator<<的重载,当你调用调度模板时,无论如何都会选择正确的重载.但这不会发生在常规函数中。 -
@Channel:确实如此。再说一次,你的例子没有可比性。如果有一个通用的
operator<<然后是一个通用的调度程序,则不会通过调度程序找到进一步的重载,就像您的示例一样。但是标准库中既没有通用的operator<<,也没有通用的operator<<调度程序。正如您的评论所暗示的,添加通用调度程序只是等式的一半。您仍然缺少一个可以调用的通用函数。 -
@Gman,好的,我稍微扩展了问题以详细说明我在说什么。请注意,调度函数是在重载之前定义的。但是编译器无论如何都会做正确的事情。但是,这似乎只适用于运算符重载 - 而不是常规函数重载。
标签: c++ function overloading