我正在阅读链接的问题,这导致我提出这个问题。
考虑以下代码
int main()
{
string SomeString();
}
总而言之,编译器将其视为函数原型,而不是string对象。现在考虑以下代码。
int main()
{
string Some()
{
return "";
}
}
编译器说这是无效的,因为我猜嵌套函数定义是不允许的。 如果不允许,为什么允许嵌套函数原型?它没有提供任何优势而不是造成混乱(或者我在这里遗漏了一些有效点?)。
我发现以下内容是有效的。
int main()
{
string SomeFun();
SomeFun();
return 0;
}
string SomeFun()
{
std::cout << "WOW this is unexpected" << std::endl;
}
这也令人困惑。我期望函数 SomeFun() 将仅在 main 中具有作用域。但我错了。为什么编译器允许编译上面这样的代码?在任何实时情况下,上面的代码是否有意义?
有什么想法吗?
你的原型只是“前向声明”。 请查看维基百科文章。
基本上,它告诉编译器“如果以这种方式使用标签‘SomeFun’,请不要惊慌”。但是你的链接器负责找到正确的函数体。
您实际上可以声明一个伪造的原型,例如'char SomeFun()' 并在你的 main.c 文件中使用它。 仅当链接器尝试查找虚假函数的主体时,您才会收到错误。 但你的编译器会很乐意接受它。
有很多好处。 您必须记住,函数体并不总是位于同一个源代码文件中。 它可以位于链接库中。此外,该链接库可能具有特定的“链接签名”。使用条件定义,您甚至可以在构建时使用作用域原型选择正确的链接签名。尽管大多数人会使用函数指针相反。
希望这有帮助。
顺便说一句,C++03 确实有一种定义局部函数的迂回方式。它需要滥用本地类功能:
int main()
{
struct Local
{
static string Some()
{
return "";
}
};
std::cout << Local::Some() << std::endl;
}
这是来自 C 的约定(与许多约定一样),C++ 已采用该约定。
在 C 语言中在另一个函数中声明一个函数的能力是大多数程序员可能认为令人遗憾和不必要的决定。 特别是在现代 OOP 设计中,函数定义比 C 中的要小。
如果您希望函数仅存在于另一个函数的作用域中,则两个选项是 boost::lambda 和 C++1x lambda。
至于你为何声明
void f() {
void g(); g();
}
比这个好
void g();
void f() {
g();
}
如果您尽可能将声明保持本地化,那么通常会很好,这样会导致尽可能少的名称冲突。我说在本地声明一个函数(以这种方式)是否真的很幸运,这是有争议的,因为我认为普通包含其标头然后采用“通常”的方式仍然更好,这对于不了解这一点的人来说也不会那么混乱。有时,解决隐藏函数也很有用
void f() {
int g;
// oops, ::g is shadowed. But we can work around that
{
void g(); g();
}
}
当然,在 C++ 中,我们可以使用
g
调用函数
its_namespace::g()
- 但在过去的 C 时代,这是不可能的,而且这允许程序员仍然访问该函数。另请注意,虽然在语法上它不一样,但从语义上讲,以下内容也声明了本地范围内的函数,该函数实际上针对不同的范围。 int main() {
using std::exit;
exit();
}
作为旁注,还有更多这样的情况:声明的目标范围不是
该声明出现的范围。通常,您声明的实体成为该声明出现的范围的成员。但情况并非总是如此。例如,考虑朋友声明,事情发生的地方
struct X { friend void f() { std::cout << "WoW"; } };
int main() { void f(); f(); } // works!
即使
f
的函数声明(和定义!)发生在
X
的范围内,实体(函数本身)也成为封闭名称空间的成员。 。它们表明这些功能即使尚未“发现”,也已在其他地方实现。仅此而已。 当您声明原型时,您基本上是在告诉编译器等待链接器解析它。范围规则的适用取决于您编写原型的位置。在 main() 函数中编写原型在技术上没有任何问题(尽管恕我直言有点混乱),它只是意味着该函数仅在 main() 内部本地已知。如果您在源文件的顶部(或更常见的是在头文件中)声明了原型,则原型/函数在整个源代码中都是已知的。