7

我正在阅读导致我提出这个问题的链接问题。

考虑以下代码

int main()
{
    string SomeString();
}

总而言之,编译器将此作为函数原型而不是字符串对象。现在考虑以下代码。

int main()
{
    string Some()
    {
        return "";
    }
}

编译器说这是无效的,因为我猜嵌套函数定义是不允许的。如果不允许,为什么允许嵌套函数原型?它没有带来任何优势,而是造成混乱(或者我在这里遗漏了一些有效的观点?)。

我发现以下是有效的。

int main()
{ 
  string SomeFun();
  SomeFun();
  return 0;
}

string SomeFun()
{
  std::cout << "WOW this is unexpected" << std::endl;
}

这也令人困惑。我期待函数SomeFun()将仅在main中具有范围。但是我错了。为什么编译器允许编译上述代码?有没有像上面这样的代码有意义的实时情况?

有什么想法吗?

4

6 回答 6

8

您的原型只是“前向声明”。请查看维基百科的文章。

基本上,它告诉编译器“如果以这种方式使用标签‘SomeFun’,请不要惊慌”。 但是您的链接器负责找到正确的函数体。

你实际上可以声明一个虚假的原型,例如'char SomeFun()',并在你的主目录中使用它。只有当您的链接器试图找到您的虚假函数的主体时,您才会收到错误消息。但是你的编译器会很酷。

有很多好处。你必须记住函数体并不总是在同一个源代码文件中。它可以在链接库中。此外,该链接库可能具有特定的“链接签名”。使用条件定义,您甚至可以在构建时使用范围内的原型选择正确的链接签名。尽管大多数人会使用函数指针相反。

希望这可以帮助。

于 2009-05-30T04:42:18.130 回答
7

顺便说一句,C++03 确实有一种迂回的方式来定义局部函数。它需要滥用本地类功能:

int main()
{
    struct Local
    {
        static string Some()
        {
            return "";
        }
    };
    std::cout << Local::Some() << std::endl;
}
于 2009-05-30T09:38:19.507 回答
5

这是 C++ 所采用的 C 约定——就像许多约定一样。

在 C 语言的另一个函数中声明一个函数的能力是大多数程序员可能认为令人遗憾和不必要的决定。特别是在现代 OOP 设计中,函数定义比 C 中的要小。

如果您想拥有仅存在于另一个函数范围内的函数,则boost::lambdaC++1x lambda有两个选项。

于 2009-05-30T04:35:50.417 回答
5

至于为什么你的声明

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,实体(函数本身)也成为封闭命名空间的成员。

于 2009-05-30T15:12:44.730 回答
3

函数原型是编译器的提示。它们表明这些功能在其他地方实现,如果尚未发现的话。而已。

于 2009-05-30T04:42:26.057 回答
3

当你声明一个原型时,你基本上是在告诉编译器等待链接器解决它。根据您编写原型的位置,范围规则适用。在 main() 函数中编写原型在技术上没有任何问题(尽管恕我直言有点混乱),它只是意味着该函数仅在 main() 内部是本地已知的。如果您在源文件的顶部(或更常见的是在头文件中)声明原型,则原型/函数将在整个源文件中是已知的。

string foo()
{
  string ret = someString();  // Error
  return ret; 
}

int main(int argc,char**argv)
{
   string someString();
   string s = somestring(); // OK
   ...
}
于 2009-05-30T08:07:06.090 回答