C++ 为什么我不能在另一个函数中定义一个函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29967202/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 21:15:43  来源:igfitidea点击:

Why can't I define a function inside another function?

c++functorfunction-declaration

提问by Jonathan Mee

This is not a lambda function question, I know that I can assign a lambda to a variable.

这不是 lambda 函数问题,我知道我可以将 lambda 分配给变量。

What's the point of allowing us to declare, but not define a function inside code?

允许我们在代码中声明但不定义函数有什么意义?

For example:

例如:

#include <iostream>

int main()
{
    // This is illegal
    // int one(int bar) { return 13 + bar; }

    // This is legal, but why would I want this?
    int two(int bar);

    // This gets the job done but man it's complicated
    class three{
        int m_iBar;
    public:
        three(int bar):m_iBar(13 + bar){}
        operator int(){return m_iBar;}
    }; 

    std::cout << three(42) << '\n';
    return 0;
}

So what I want to know is why would C++ allow twowhich seems useless, and threewhich seems far more complicated, but disallow one?

所以我想知道的是为什么 C++ 会允许two看起来没用的,three看起来更复杂的,但不允许的one

EDIT:

编辑:

From the answers it seems that there in-code declaration may be able to prevent namespace pollution, what I was hoping to hear though is why the ability to declare functions has been allowed but the ability to define functions has been disallowed.

从答案看来,代码中声明可能能够防止命名空间污染,但我希望听到的是为什么允许声明函数的能力但不允许定义函数的能力。

采纳答案by Shafik Yaghmour

It is not obvious why oneis not allowed; nested functions were proposed a long time ago in N0295which says:

为什么one不被允许并不明显;很久以前在N0295中提出了嵌套函数,它说:

We discuss the introduction of nested functions into C++. Nested functions are well understood and their introduction requires little effort from either compiler vendors, programmers, or the committee. Nested functions offer significant advantages, [...]

我们讨论将嵌套函数引入 C++。嵌套函数很好理解,它们的引入几乎不需要编译器供应商、程序员或委员会的努力。嵌套函数提供了显着的优势,[...]

Obviously this proposal was rejected, but since we don't have meeting minutes available online for 1993we don't have a possible source for the rationale for this rejection.

显然,该提案被拒绝了,但由于我们没有在线可用的会议纪要,因为我们没有1993可能的来源来说明拒绝的理由。

In fact this proposal is noted in Lambda expressions and closures for C ++as a possible alternative:

事实上,这个提议在C++ 的 Lambda 表达式和闭包中被指出作为一种可能的替代方案:

One article [Bre88] and proposal N0295 to the C ++ committee [SH93] suggest adding nested functions to C ++ . Nested functions are similar to lambda expressions, but are defined as statements within a function body, and the resulting closure cannot be used unless that function is active. These proposals also do not include adding a new type for each lambda expression, but instead implementing them more like normal functions, including allowing a special kind of function pointer to refer to them. Both of these proposals predate the addition of templates to C ++ , and so do not mention the use of nested functions in combination with generic algorithms. Also, these proposals have no way to copy local variables into a closure, and so the nested functions they produce are completely unusable outside their enclosing function

一篇文章 [Bre88] 和提交给 C++ 委员会 [SH93] 的提案 N0295 建议向 C++ 添加嵌套函数。嵌套函数类似于 lambda 表达式,但被定义为函数体内的语句,除非该函数处于活动状态,否则无法使用生成的闭包。这些提议也不包括为每个 lambda 表达式添加新类型,而是更像普通函数一样实现它们,包括允许特殊类型的函数指针引用它们。这两个提议都早于向 C++ 添加模板,因此没有提到将嵌套函数与泛型算法结合使用。此外,这些提议无法将局部变量复制到闭包中,因此它们生成的嵌套函数在其封闭函数之外完全无法使用

Considering we do now have lambdas we are unlikely to see nested functions since, as the paper outlines, they are alternatives for the same problem and nested functions have several limitations relative to lambdas.

考虑到我们现在有 lambda,我们不太可能看到嵌套函数,因为正如论文概述的那样,它们是同一问题的替代方案,并且嵌套函数相对于 lambda 有几个限制。

As for this part of your question:

至于你问题的这一部分:

// This is legal, but why would I want this?
int two(int bar);
// This is legal, but why would I want this?
int two(int bar);

There are cases where this would be a useful way to call the function you want. The draft C++ standard section 3.4.1[basic.lookup.unqual]gives us one interesting example:

在某些情况下,这将是调用所需函数的有用方法。C++ 标准部分草案3.4.1[basic.lookup.unqual]给了我们一个有趣的例子:

namespace NS {
    class T { };
    void f(T);
    void g(T, int);
}

NS::T parm;
void g(NS::T, float);

int main() {
    f(parm); // OK: calls NS::f
    extern void g(NS::T, float);
    g(parm, 1); // OK: calls g(NS::T, float)
}

回答by M.M

Well, the answer is "historical reasons". In C you could have function declarations at block scope, and the C++ designers did not see the benefit in removing that option.

嗯,答案是“历史原因”。在 C 中,您可以在块范围内进行函数声明,而 C++ 设计者没有看到删除该选项的好处。

An example usage would be:

一个示例用法是:

#include <iostream>

int main()
{
    int func();
    func();
}

int func()
{
    std::cout << "Hello\n";
}

IMO this is a bad idea because it is easy to make a mistake by providing a declaration that does not match the function's real definition, leading to undefined behaviour which will not be diagnosed by the compiler.

IMO 这是一个坏主意,因为通过提供与函数的实际定义不匹配的声明很容易犯错误,从而导致编译器无法诊断的未定义行为。

回答by Richard Hodges

In the example you give, void two(int)is being declared as an external function, with that declaration only being valid within the scope of the mainfunction.

在您给出的示例中,void two(int)被声明为外部函数,该声明仅在main函数范围内有效

That's reasonable if you only wish to make the name twoavailable within main()so as to avoid polluting the global namespace within the current compilation unit.

这是合理的,如果你只是想使名称two可用的范围内main(),以避免污染当前编译单元中的全局命名空间。

Example in response to comments:

回复评论的例子:

main.cpp:

主.cpp:

int main() {
  int foo();
  return foo();
}

foo.cpp:

foo.cpp:

int foo() {
  return 0;
}

no need for header files. compile and link with

不需要头文件。编译并链接

c++ main.cpp foo.cpp 

it'll compile and run, and the program will return 0 as expected.

它将编译并运行,并且程序将按预期返回 0。

回答by Jerry Coffin

You can do these things, largely because they're actually not all that difficult to do.

你可以做这些事情,主要是因为它们实际上并不难做到。

From the viewpoint of the compiler, having a function declaration inside another function is pretty trivial to implement. The compiler needs a mechanism to allow declarations inside of functions to handle other declarations (e.g., int x;) inside a function anyway.

从编译器的角度来看,在另一个函数中声明一个函数是很容易实现的。编译器需要一种机制来允许函数内部的声明int x;无论如何处理函数内部的其他声明(例如,)。

It will typically have a general mechanism for parsing a declaration. For the guy writing the compiler, it doesn't really matter at all whether that mechanism is invoked when parsing code inside or outside of another function--it's just a declaration, so when it sees enough to know that what's there is a declaration, it invokes the part of the compiler that deals with declarations.

它通常具有用于解析声明的通用机制。对于编写编译器的人来说,在解析另一个函数内部或外部的代码时是否调用该机制根本无关紧要——它只是一个声明,所以当它看到足够多的时候知道什么是声明,它调用处理声明的编译器部分。

In fact, prohibiting these particular declarations inside a function would probably add extra complexity, because the compiler would then need an entirely gratuitous check to see if it's already looking at code inside a function definition and based on that decide whether to allow or prohibit this particular declaration.

事实上,在函数内部禁止这些特定的声明可能会增加额外的复杂性,因为编译器需要一个完全免费的检查来查看它是否已经在查看函数定义中的代码,并基于此决定是允许还是禁止这个特定的声明宣言。

That leaves the question of how a nested function is different. A nested function is different because of how it affects code generation. In languages that allow nested functions (e.g., Pascal) you normally expect that the code in the nested function has direct access to the variables of the function in which it's nested. For example:

这就留下了嵌套函数有何不同的问题。嵌套函数是不同的,因为它会影响代码生成。在允许嵌套函数的语言(例如 Pascal)中,您通常希望嵌套函数中的代码可以直接访问嵌套函数的变量。例如:

int foo() { 
    int x;

    int bar() { 
        x = 1; // Should assign to the `x` defined in `foo`.
    }
}

Without local functions, the code to access local variables is fairly simple. In a typical implementation, when execution enters the function, some block of space for local variables is allocated on the stack. All the local variables are allocated in that single block, and each variable is treated as simply an offset from the beginning (or end) of the block. For example, let's consider a function something like this:

没有局部函数,访问局部变量的代码相当简单。在典型的实现中,当执行进入函数时,会在堆栈上为局部变量分配一些空间块。所有局部变量都分配在该单个块中,并且每个变量都被视为距块开头(或结尾)的一个偏移量。例如,让我们考虑一个这样的函数:

int f() { 
   int x;
   int y;
   x = 1;
   y = x;
   return y;
}

A compiler (assuming it didn't optimize away the extra code) might generate code for this roughly equivalent to this:

编译器(假设它没有优化掉多余的代码)可能会为此生成与此大致等效的代码:

stack_pointer -= 2 * sizeof(int);      // allocate space for local variables
x_offset = 0;
y_offset = sizeof(int);

stack_pointer[x_offset] = 1;                           // x = 1;
stack_pointer[y_offset] = stack_pointer[x_offset];     // y = x;
return_location = stack_pointer[y_offset];             // return y;
stack_pointer += 2 * sizeof(int);

In particular, it has onelocation pointing to the beginning of the block of local variables, and all access to the local variables is as offsets from that location.

特别是,它有一个位置指向局部变量块的开头,并且对局部变量的所有访问都是从该位置开始的偏移量。

With nested functions, that's no longer the case--instead, a function has access not only to its own local variables, but to the variables local to all the functions in which it's nested. Instead of just having one "stack_pointer" from which it computes an offset, it needs to walk back up the stack to find the stack_pointers local to the functions in which it's nested.

对于嵌套函数,情况不再如此——相反,函数不仅可以访问它自己的局部变量,还可以访问它嵌套在其中的所有函数的局部变量。它不仅需要一个“stack_pointer”来计算偏移量,还需要返回堆栈以找到嵌套在其中的函数的本地 stack_pointers。

Now, in a trivial case that's not all that terrible either--if baris nested inside of foo, then barcan just look up the stack at the previous stack pointer to access foo's variables. Right?

现在,在一个微不足道的情况下,这也不是那么糟糕——如果bar嵌套在 中foo,则bar可以在前一个堆栈指针处查找堆栈以访问foo的变量。对?

Wrong!Well, there are cases where this can be true, but it's not necessarily the case. In particular, barcould be recursive, in which case a given invocation of barmight have to look some nearly arbitrary number of levels back up the stack to find the variables of the surrounding function. Generally speaking, you need to do one of two things: either you put some extra data on the stack, so it can search back up the stack at run-time to find its surrounding function's stack frame, or else you effectively pass a pointer to the surrounding function's stack frame as a hidden parameter to the nested function. Oh, but there's not necessarily just one surrounding function either--if you can nest functions, you can probably nest them (more or less) arbitrarily deep, so you need to be ready to pass an arbitrary number of hidden parameters. That means you typically end up with something like a linked list of stack frames to surrounding functions, and access to variables of surrounding functions is done by walking that linked list to find its stack pointer, then accessing an offset from that stack pointer.

错误的!嗯,在某些情况下这可能是真的,但不一定是这样。特别是,bar可以是递归的,在这种情况下,给定的调用bar可能必须在堆栈中查找一些几乎任意数量的级别才能找到周围函数的变量。一般来说,你需要做两件事之一:要么在堆栈上放一些额外的数据,这样它就可以在运行时搜索堆栈以找到它周围函数的堆栈帧,或者你有效地传递一个指针到周围函数的堆栈帧作为嵌套函数的隐藏参数。哦,但也不一定只有一个周围的函数——如果你可以嵌套函数,你可能可以将它们嵌套(或多或少)任意深,所以你需要准备好传递任意数量的隐藏参数。这意味着您通常最终会得到类似堆栈帧链接列表到周围函数的东西,

That, however, means that access to a "local" variable may not be a trivial matter. Finding the correct stack frame to access the variable can be non-trivial, so access to variables of surrounding functions is also (at least usually) slower than access to truly local variables. And, of course, the compiler has to generate code to find the right stack frames, access variables via any of an arbitrary number of stack frames, and so on.

然而,这意味着访问“本地”变量可能不是一件小事。找到正确的堆栈帧来访问变量可能很重要,因此访问周围函数的变量也(至少通常)比访问真正的局部变量慢。而且,当然,编译器必须生成代码以找到正确的堆栈帧,通过任意数量的堆栈帧中的任何一个访问变量,等等。

Thisis the complexity that C was avoiding by prohibiting nested functions. Now, it's certainly true that a current C++ compiler is a rather different sort of beast from a 1970's vintage C compiler. With things like multiple, virtual inheritance, a C++ compiler has to deal with things on this same general nature in any case (i.e., finding the location of a base-class variable in such cases can be non-trivial as well). On a percentage basis, supporting nested functions wouldn't add much complexity to a current C++ compiler (and some, such as gcc, already support them).

就是 C 通过禁止嵌套函数来避免的复杂性。现在,可以肯定的是,当前的 C++ 编译器与 1970 年代的老式 C 编译器完全不同。对于多重、虚拟继承之类的事情,C++ 编译器在任何情况下都必须处理具有相同一般性质的事情(即,在这种情况下查找基类变量的位置也很重要)。在百分比的基础上,支持嵌套函数不会给当前的 C++ 编译器增加太多复杂性(并且一些,例如 gcc,已经支持它们)。

At the same time, it rarely adds much utility either. In particular, if you want to define something that actslike a function inside of a function, you can use a lambda expression. What this actually creates is an object (i.e., an instance of some class) that overloads the function call operator (operator()) but it still gives function-like capabilities. It makes capturing (or not) data from the surrounding context more explicit though, which allows it to use existing mechanisms rather than inventing a whole new mechanism and set of rules for its use.

同时,它也很少增加很多实用性。特别是,如果你想定义的东西,行为像一个函数的功能里面,你可以使用lambda表达式。这实际上创建的是一个对象(即某个类的实例),它重载了函数调用运算符 ( operator()) 但它仍然提供了类似函数的功能。它使得从周围上下文中捕获(或不捕获)数据更加明确,这允许它使用现有机制,而不是发明一个全新的机制和一套使用规则。

Bottom line: even though it might initially seem like nested declarations are hard and nested functions are trivial, more or less the opposite is true: nested functions are actually much more complex to support than nested declarations.

底线:尽管最初看起来嵌套声明很困难并且嵌套函数很简单,但或多或​​少恰恰相反:嵌套函数实际上比嵌套声明要复杂得多。

回答by ANjaNA

The first one is a function definition, and it is not allowed. Obvious, wt is the usage of putting a definition of a function inside another function.

第一个是函数定义,这是不允许的。显然,wt 是将一个函数的定义放在另一个函数中的用法。

But the other twos are just declarations. Imagine you need to use int two(int bar);function inside the main method. But it is defined below the main()function, so that function declaration inside the function makes you to use that function with declarations.

但其他两个只是声明。想象一下,您需要int two(int bar);在 main 方法中使用函数。但是它是在main()函数下面定义的,因此函数内部的函数声明使您可以使用带有声明的函数。

The same applies to the third. Class declarations inside the function allows you to use a class inside the function without providing an appropriate header or reference.

这同样适用于第三个。函数内的类声明允许您在函数内使用类,而无需提供适当的头文件或引用。

int main()
{
    // This is legal, but why would I want this?
    int two(int bar);

    //Call two
    int x = two(7);

    class three {
        int m_iBar;
        public:
            three(int bar):m_iBar(13 + bar) {}
            operator int() {return m_iBar;}
    };

    //Use class
    three *threeObj = new three();

    return 0;
}

回答by Peter - Reinstate Monica

Actually, there is one use case which is conceivably useful. If you want to make sure that a certain function is called (and your code compiles), no matter what the surrounding code declares, you can open your own block and declare the function prototype in it. (The inspiration is originally from Johannes Schaub, https://stackoverflow.com/a/929902/3150802, via TeKa, https://stackoverflow.com/a/8821992/3150802).

实际上,有一个用例非常有用。如果你想确保某个函数被调用(并且你的代码编译),无论周围的代码声明了什么,你都可以打开你自己的块并在其中声明函数原型。(灵感最初来自 Johannes Schaub,https://stackoverflow.com/a/929902/3150802,来自TeKa,https://stackoverflow.com/a/8821992/3150802 )。

This may be particularily useful if you have to include headers which you don't control, or if you have a multi-line macro which may be used in unknown code.

如果您必须包含您无法控制的标题,或者您有一个可能在未知代码中使用的多行宏,这可能特别有用。

The key is that a local declaration supersedes previous declarations in the innermost enclosing block. While that can introduce subtle bugs (and, I think, is forbidden in C#), it can be used consciously. Consider:

关键是局部声明取代了最内部封闭块中的先前声明。虽然这可能会引入微妙的错误(我认为,在 C# 中是禁止的),但可以有意识地使用它。考虑:

// somebody's header
void f();

// your code
{   int i;
    int f(); // your different f()!
    i = f();
    // ...
}

Linking may be interesting because chances are the headers belong to a library, but I guess you can adjust the linker arguments so that f()is resolved to your function by the time that library is considered. Or you tell it to ignore duplicate symbols. Or you don't link against the library.

链接可能很有趣,因为头文件可能属于某个库,但我想您可以调整链接器参数,以便f()在考虑该库时将其解析为您的函数。或者你告诉它忽略重复的符号。或者你不链接图书馆。

回答by mr.pd

This language feature was inherited from C, where it served some purpose in C's early days (function declaration scoping maybe?). I don't know if this feature is used much by modern C programmers and I sincerely doubt it.

这个语言特性是从 C 继承的,它在 C 的早期起到了一些作用(可能是函数声明范围?)。我不知道现代 C 程序员是否经常使用此功能,我真诚地怀疑它。

So, to sum up the answer:

所以,总结一下答案:

there is no purpose for this feature in modernC++ (that I know of, at least), it is here because of C++-to-C backward compatibility (I suppose :) ).

现代C++ 中没有这个特性(至少我知道),它在这里是因为 C++ 到 C 的向后兼容性(我想:))。



Thanks to the comment below:

感谢以下评论:

Function prototype is scoped to the function it is declared in, so one can have a tidier global namespace - by referring to external functions/symbols without #include.

函数原型的作用域限定于它在其中声明的函数,因此可以拥有一个更整洁的全局命名空间——通过引用外部函数/符号而不带#include.

回答by Michael

This is not an answer to the OP question, but rather a reply to several comments.

这不是对 OP 问题的回答,而是对几条评论的回复。

I disagree with these points in the comments and answers: 1that nested declarations are allegedly harmless, and 2that nested definitions are useless.

我不同意评论和答案中的这些观点:1嵌套声明据称是无害的,2嵌套定义是无用的。

1The prime counterexample for the alleged harmlessness of nested function declarations is the infamous Most Vexing Parse. IMO the spread of confusion caused by it is enough to warrant an extra rule forbidding nested declarations.

1声称嵌套函数声明无害的主要反例是臭名昭著的 Most Vexing Parse。IMO 由它引起的混乱的蔓延足以保证禁止嵌套声明的额外规则。

2The 1st counterexample to the alleged uselessness of nested function definitions is frequent need to perform the same operation in several places inside exactly one function. There is an obvious workaround for this:

2所谓嵌套函数定义无用的第一个反例是经常需要在一个函数内的多个地方执行相同的操作。对此有一个明显的解决方法:

private:
inline void bar(int abc)
{
    // Do the repeating operation
}

public: 
void foo()
{
    int a, b, c;
    bar(a);
    bar(b);
    bar(c);
}

However, this solution often enough contaminates the class definition with numerous private functions, each of which is used in exactly one caller. A nested function declaration would be much cleaner.

然而,这种解决方案通常会用大量私有函数污染类定义,每个私有函数都只在一个调用者中使用。嵌套函数声明会更简洁。

回答by Richard Hodges

Specifically answering this question:

具体回答这个问题:

From the answers it seems that there in-code declaration may be able to prevent namespace pollution, what I was hoping to hear though is why the ability to declare functions has been allowed but the ability to define functions has been disallowed.

从答案看来,代码中声明可能能够防止命名空间污染,但我希望听到的是为什么允许声明函数的能力但不允许定义函数的能力。

Because consider this code:

因为考虑这个代码:

int main()
{
  int foo() {

    // Do something
    return 0;
  }
  return 0;
}

Questions for language designers:

语言设计师的问题:

  1. Should foo()be available to other functions?
  2. If so, what should be its name? int main(void)::foo()?
  3. (Note that 2 would not be possible in C, the originator of C++)
  4. If we want a local function, we already have a way - make it a static member of a locally-defined class. So should we add another syntactic method of achieving the same result? Why do that? Wouldn't it increase the maintenance burden of C++ compiler developers?
  5. And so on...
  1. 应该foo()可以用于其他功能吗?
  2. 如果是这样,它的名字应该是什么?int main(void)::foo()?
  3. (请注意,在 C++ 的鼻祖 C 中,2 是不可能的)
  4. 如果我们想要一个本地函数,我们已经有办法了——让它成为本地定义类的静态成员。那么我们是否应该添加另一种实现相同结果的句法方法?为什么要这样做?不会增加C++编译器开发者的维护负担吗?
  5. 等等...

回答by smac89

Just wanted to point out that the GCC compiler allows you to declare functions inside functions. Read more about it here. Also with the introduction of lambdasto C++, this question is a bit obsolete now.

只是想指出 GCC 编译器允许您在函数内声明函数。在此处阅读更多相关信息。随着lambdas引入C++,这个问题现在有点过时了。



The ability to declare function headers inside other functions, I found useful in the following case:

在其他函数中声明函数头的能力,我发现在以下情况下很有用:

void do_something(int&);

int main() {
    int my_number = 10 * 10 * 10;
    do_something(my_number);

    return 0;
}

void do_something(int& num) {
    void do_something_helper(int&); // declare helper here
    do_something_helper(num);

    // Do something else
}

void do_something_helper(int& num) {
    num += std::abs(num - 1337);
}

What do we have here? Basically, you have a function that is supposed to be called from main, so what you do is that you forward declare it like normal. But then you realize, this function also needs another function to help it with what it's doing. So rather than declaring that helper function above main, you declare it inside the function that needs it and then it can be called from that function and that function only.

我们有什么在这里?基本上,你有一个应该从 main 调用的函数,所以你要做的是像往常一样转发声明它。但后来你意识到,这个函数还需要另一个函数来帮助它完成它正在做的事情。因此,与其在 main 之上声明辅助函数,不如在需要它的函数中声明它,然后才能从该函数和该函数中调用它。

My point is, declaring function headers inside functions can be an indirect method of function encapsulation, which allows a function to hide some parts of what it's doing by delegating to some other function that only it is aware of, almost giving an illusion of a nested function.

我的观点是,在函数内部声明函数头可以是一种函数封装的间接方法,它允许函数通过委托给其他只有它知道的函数来隐藏它正在做的某些部分,几乎给人一种嵌套的错觉功能