Why can't I define a function inside another function?
Asked Answered
S

11

83

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

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 two which seems useless, and three which seems far more complicated, but disallow 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.

Spada answered 30/4, 2015 at 11:58 Comment(28)
The first, one is a function definition, the other two are declarations.Cossack
I think you got the terms the wrong way -- you want to ask "What's the point of allowing us to declare, but not define a function inside code?". And while we are at it, you probably mean "inside a function". It's all "code".Clock
If you're asking why the language has quirks and inconsistencies: because it evolved over several decades, through the work of many people with many different ideas, from languages invented for different reasons at different times. If you're asking why it has this particular quirk: because no-one (so far) thought local function definitions were useful enough to standardise.Huberty
@MikeSeymour has it properly right. C is not as well structured as, say, Pascal, and always allowed only top-level function definitions. So the reason is historic, plus a lacking need to change it. That function declarations are possible is just a consequence of scoped declarations in general being possible. Prohibiting that for functions would have meant an extra rule.Clock
@MikeSeymour Sounds like the best answer so far. But I still don't get why they allowed function declaration in the code though.Spada
@JonathanMee: Probably because, in general, declarations are allowed in blocks, and there's no particular reason to specifically forbid function declarations; it's simpler to just allow any declaration with no special cases. But "why" isn't really an answerable question; the language is what it is because that's how it evolved.Huberty
In practice you would not use class three, you'd use a lambda ... that's why they were added, to reduce exactly this verbiage. Also this question has nothing at all to do with MVP.Canberra
@MattMcNabb I agree that I'd use a lambda, I was thinking there was some deeper mechanism of the language that I was missing. Isn't Most Vexing Parse where a the programmer thinks he is constructing a variable and accidentally declares a function? Seems related to me?Spada
possible duplicate of Is there a use for function declarations inside functions?Steelworker
There are ADL cases where this matters ... see the example at the end of my answer.Speedball
@ShafikYaghmour I read through your answer, and learned a little something about ADL, but I can't see how this pertains.Spada
@JonathanMee well it an example that covers your comment This is legal, but why would I want thisSpeedball
Defining function was proposed a long time ago but since we don't have mintues for that meeting we don't know why it was rejected.Speedball
@ShafikYaghmour That would make a pretty great answer.Spada
Aside: The sample code does not invoke the most vexing parse. Please do not add that tag back in.Erudition
Regarding int two(int) -- there are multiple reasons why this is allowed. One is that it makes parsing easier (Jerry Coffin's answer). Another is that because it is allowed, there's lots of legacy code (and even some modern code) that uses such constructs. A very compelling case needs to be made for a proposed change that revokes backwards compatibility.Erudition
Regarding three(42) -- This is not the most vexing parse. three(42) creates an instance of class three. There is no stream insertion operator for this class (e.g., std::ostream& operator<<(std::ostream&, const three&)), so the compiler looks for a conversion operator that will convert an object of type three to a type for which a stream insertion operator does exist. That of course three::operator int().Erudition
Actually, nested functions are available as a GNU extension in gcc. This has nothing to do with why standard C/C++ allow you to declare functions inside function definitions, but is worth mentioning. See gcc.gnu.org/onlinedocs/gcc/Nested-Functions.htmlTabular
@Tabular note that the document says but are not supported by GNU C++. .Speedball
@DavidHammen Most Vexing Parse(MVP) is hat annoys me about this, that was edited out of an early version of the question (not by me.) Part of my question was why allow in code function defines at all if all they do is incite MVP and you can't define them anyway. After understanding some of the answers here I understand why we need in code definitions, however it perplexes me why no one sees that this has to do with MVP.Spada
@JonathanMee - In the original version of the question, you didn't use that wording. What you wrote about most vexing parse instead looked out of place. What you apparently want is for int f(); to be a function declaration when used at namespace scope but a variable declaration when used at function scope. That's a bit scary.Erudition
@JonathanMee - Regarding int two(int bar); you asked "This is legal, but why would I want this?" In modern code, you don't want to do this. The problem is that not all code compiled by the compiler is modern code. There's lots and lots of old code out there that does exactly this. In the early days of C, some even advocated that this was the "right way" to declare functions as it lets readers of the code see a function's fanout right up front. Some of that code still exists. And it still compiles, as-is.Erudition
I didn't see it mentioned, but there's a much easier way of using the class approach, struct {int operator()(int i) {return i;}} func; func(42);, see: ideone.com/0j8kTKEquidistant
@Equidistant Yeah you are correct; I realized that after I wrote it, but decided against modifying the question cause this really isn't about how to more effectively write an in code class, it's about why can't I define a function in code when I can declare a function in code.Spada
@DavidHammen The question is a simplified version of what is linked. What I wanted to know is, why do I have to put up with the aggravation of MVP and I can't get the benefits of in code function definition. I think my question has been answered, so I'm not going to continue to go back and forth with people editing this. But I feel sad that it had to be defaced by people who thought they understood my question better than I did :(Spada
@JonathanMee - You have to put up with it because people wrote code that used this exact construct 30 or even 40 years ago. That code still compiles, sometimes with a lot of warnings, but it still compiles. There's a lot to be said against backwards compatibility, but there's a lot more to be said for it. Money talks. Having to rewrite that legacy code would cost lots and lots and lots of money. It would cost enough money to justify a campaign to fire every member of the C++ standards committee who approved of your desired change fired -- and then the standard would be reverted.Erudition
@DavidHammen Yeah, I didn't understand the usefulness of it until I read some of the comments. As you said this was even advocated by some as the "right way" to limit scope. Welp, that's why I ask questions on here to learn stuff. There's an awful lot of knowledge in some of these answers, and for that I'm very grateful.Spada
Nowadays, those old ideas of how to limit scope are viewed as old ideas. When you asked "why would I want to do this", the answer is you don't. Those are ideas from a previous millennium. We humans try lots of ideas. Some turn out to be fantastic, others OK, yet others, blech! If we only could know ahead of time which was which ...Erudition
S
42

It is not obvious why one is not allowed; nested functions were proposed a long time ago in N0295 which says:

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, [...]

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

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

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

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.

As for this part of your question:

// 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:

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)
}
Speedball answered 30/4, 2015 at 14:30 Comment(9)
Question wrt the 3.4.1 example you give: Couldn't the caller in main simply write ::g(parm, 1) in order to call the function in the global namespace? Or call g(parm, 1.0f); which should result in a better match for the desired g?Clock
@PeterSchneider I made too strong a statement there, I adjusted it.Speedball
I'd like to add the comment here: This answer was accepted not because it did the best job of explaining why in code function declarations are allowed; but because it did the best job describing why in code function definitions are not allowed, which was the actual question. And specifically it specifically outlines why the hypothetical implementation of in code functions would differ from the implementation of lambdas. +1Spada
@JonathanMee: How in the world does: "...we don't have a possible source for the rationale for this rejection." qualify as the best job of describing why nested function definitions aren't allowed (or even attempting to describe it at all?)Schilit
@JerryCoffin The answer included the official rationale of why lambdas are already a super set of in code function definitions rendering their implementation unnecessary: "The resulting closure cannot be used unless that function is active... Also, these proposals have no way to copy local variables into a closure." I assume that you're asking why your analysis of the additional complexity placed upon compilers was not the answer I accepted. If so: You speak to the difficulty of something lambdas already accomplish, in code definitions could clearly be implemented exactly like lambdas.Spada
@JonathanMee: The decision(s) to reject nested functions happened decades before lambdas were added in C++11 so explanations based on lambda expressions make no sense at all. Don't get me wrong: I'm not trying to tell you to accept a different answer. I'm primarily interested in seeing how I could improve future posts, and in this case the suggestion seems to be: "write posts that don't even make sense."Schilit
@JerryCoffin Let me just summarize my understanding of this answer: "In code function definitions were revisited in the development of the C++11 feature set, but they were rejected as a subset of lambda functionality." My understanding of your post: "In code function definition was rejected historically because of implementation complexity. Currently in code function definitions are available in gcc, or in standardized lambdas." These answers seem similar when I summarize them, but the clear-cut quote from the standard committee in this answer was what sold me.Spada
@JonathanMee: This seems to be completely mistaken. I'm quite certain nobody revisited the idea of adding nested functions when developing the C++11 feature set. The reasoning there was being used as justification for lambdas. In essence: "for years, a few people have wanted to added nested functions, and with lambdas, we get/provide a superset of that capability."Schilit
@JerryCoffin Your summary: "For years, a few people have wanted to added nested functions, and with lambdas, we get/provide a superset of that capability." In coloration with the this answer's statement that the standards committee had previously rejected in code function definitions, makes what I would consider a well sourced answer to my question, why has, "The ability to declare functions has been allowed but the ability to define functions has been disallowed?"Spada
C
31

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.

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.

Canberra answered 30/4, 2015 at 12:13 Comment(18)
"This is generally considered a bad idea' - citation required.Raceme
@RichardHodges: Well, function declarations belong in header files, and the implementation in .c or .cpp files, so having these declarations inside function definitions violates either of those two guidelines.Coke
header files are certainly convenient places to put function declarations but there is no language rule that stipulates that this must be the case. If you only want access to one name from another compilation unit, there's no need to import all the names exported from that compilation unit.Raceme
@RichardHodges: "there is no language rule" - but there is one that stipulates that declarations must match the corresponding definition, and doing this prevents the compiler from enforcing that rule (if the definition is in a different translation unit). Which makes it a bad idea.Huberty
How does it prevent the declaration being different to the definition?Raceme
@RichardHodges: With just one declaration in a header, you can include it in the translation unit that defines the function, allowing the compiler to check that the definition matches the declaration. Declaring it in other places prevents that check, potentially leading to undefined behaviour if you get a declaration wrong. So that's a bad idea.Huberty
@MikeSeymour Can you explain in a bit more detail, I'm having trouble keeping up. What I understood this to say is, an in code declaration of a previously declared function prevents the compiler from checking both declarations. Is that what you are saying?Spada
This is legal code. It declares 2 functions and defines one. C++ is not the same as C. int foo(); int foo(int); int foo(int) { return 0; }Raceme
@JonathanMee: I'm saying that, if the declaration you're using isn't available where the function is defined, the compiler might not be checking that the declaration matches the definition. So you might have a local declaration some_type f();, and a definition in another translation unit another_type f() {...}. The compiler can't tell you that these don't match, and calling f with the wrong declaration will give undefined behaviour. So it's a good idea to only have one declaration, in a header, and include that header where the function is defined, as well as where it's used.Huberty
@RichardHodges: But you could declare int foo() in one place, and define void foo() elsewhere. That's illegal, but the compiler won't help you if it can't see both. You'll get nasty undefined behaviour, not a friendly compile error.Huberty
I think what you're saying is that the common practice of putting function declarations in header files is generally useful. I don't think anyone would disagree with that. What I see no reason for is the assertion that declaring an external function at function scope is 'generally considered a bad idea'.Raceme
@RichardHodges: To repeat myself yet again: if the function is defined in a different translation unit then it's a bad idea because, without a declaration in a header, the compiler can't check that the declaration matches the definition. (With a declaration in a header, or the function defined in the same unit, a duplicate declaration is pointless, so still a somewhat bad idea.)Huberty
@RichardHodges re-wordedCanberra
The error you foresee would be caught at link time because the two functions would have different names (the return type is part of the name). The use-case of the local declaration is when you want one or two functions from a library without taking them all. In the old days this could help a compiler to not run out of memory (yup!!). These days it's less useful I'll grant you. But the 'bad idea' bit is opinion, not fact.Raceme
@RichardHodges: There's no guarantee that the return type forms part of the mangled name, since it isn't part of the signature. At least one popular mangling scheme (used by GCC) doesn't include the return type, so the error won't be caught by the linker, and will give undefined behaviour. So it's a bad idea.Huberty
This answer would be better without the editorial comment. IMO, disallowing it would be far worse than the current situation for the simple reason that disallowing it would mean a lot of legacy code that no longer compiles. Moreover, disallowing this won't fix the problem. What fixes the problem is disallowing declarations of functions with external linkage in source files. That's the job of a coding standard, not the language specification.Erudition
@DavidHammen I meant, apart from that issue. Have removed the comment in any case.Canberra
@MattMcNabb - With that, +1. (Note: I did not downvote prior to this. It was just a little editorial.) Apart from that issue, yes, do not do this in modern code. It would be nice to have a compiler warning along the lines of -Warchaic-legacy-nonsense (and maybe have it on by default). With that, int f() { int g(); return g();} would still compile; it would just get a warning.Erudition
R
23

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 main function.

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

Example in response to comments:

main.cpp:

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

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.

Raceme answered 30/4, 2015 at 12:18 Comment(6)
Wouldn't two also have to be defined in the file thereby causing the pollution anyway?Spada
@JonathanMee no, two() could be defined in an entirely different compilation unit.Raceme
I need help understanding how that would work. Wouldn't you have to include the header it was declared in? At which point it would be declared, right? I just don't see how you can define it in code, and somehow not include the file that declares it?Spada
@JonathanMee There's nothing special about headers. They're just a convenient place to put declarations. A declaration within a function is just as valid as a declaration within a header. So, no, you wouldn't need to include the header of what you're linking to (there may not even be a header at all).Leighleigha
@Leighleigha So you're saying that I could have an in code definition of int foo(int) in file X.h and the implementation of int foo(int) in X.cpp? I guess that makes sense.Spada
@JonathanMee In C/C++ lingo, definition and implementation are the same thing. You can declare a function as often as you want, but you can only define it once. The declaration doesn't need to be in a file ending in .h - you can have a file use.cpp that has a function bar that calls foo (declaring foo in its body), and a file provides.cpp that defines foo, and it'd work fine as long as you don't mess up the linking step.Leighleigha
S
19

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.

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:

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 one location 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.

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

Wrong! Well, there are cases where this can be true, but it's not necessarily the case. In particular, bar could be recursive, in which case a given invocation of bar might 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.

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.

This is 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).

At the same time, it rarely adds much utility either. In particular, if you want to define something that acts like 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.

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.

Schilit answered 30/4, 2015 at 13:13 Comment(0)
I
5

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.

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.

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;
}
Intermarry answered 30/4, 2015 at 12:26 Comment(1)
What is "deceleration"? Do you mean "declaration"?Grave
S
4

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.

So, to sum up the answer:

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


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.

Steelworker answered 30/4, 2015 at 12:13 Comment(2)
the purpose is controlling the scope of the name to avoid global namespace pollution.Raceme
Ok, I suppose it is useful for situations, when you want to refer to external functions/symbols without polluting the global namespace with #include! Thanks for pointing it out. I'll make an edit.Steelworker
C
4

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://mcmap.net/q/66175/-nested-functions-are-not-allowed-but-why-nested-function-prototypes-are-allowed-c, via TeKa, https://mcmap.net/q/66176/-is-there-any-use-for-local-function-declarations).

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:

// 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.

Clock answered 30/4, 2015 at 12:40 Comment(5)
So help me out here, where would f get defined in your example? Would I not end up with a function redefinition error since these differ only by return type?Spada
@JonathanMee hmmm... f() could be defined in a different translation unit, I thought. But probably the linker would balk if you also linked against the assumed library, I assume, you are right. So you cannot do that ;-), or at least have to ignore multiple definitions.Clock
Bad example. There is no distinction between void f() and int f() in C++ because a function's return value is not part of the function's signature in C++. Change the second declaration to int f(int) and I'll remove my downvote.Erudition
@DavidHammen Try to compile i = f(); after declaring void f(). "No distinction" is only half the truth ;-). I actually used non-overloadable function "signatures" because otherwise the whole circumstance would be unnecessary in C++ because two functions with different parameter types/numbers could happily coexist.Clock
@DavidHammen Indeed, after reading Shafik's answer I believe we have three cases: 1. Signature differs in parameters. No issue in C++, simple overload and best match rules work. 2. Signature does not differ at all. No issue at language level; function is resolved by linking against the desired implementation. 3. Difference is only in return type. There is an issue at the language level, as demonstrated; overload resolution doesn't work; we have to declare a function with a different signature and link appropriately.Clock
M
3

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

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

1 The 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.

2 The 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:

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.

Messeigneurs answered 1/5, 2015 at 22:26 Comment(1)
I think this makes a good summary of the motivation of my question. If you look at the original version I cited MVP, but I keep getting overruled in the comments (of my own question) being told that MVP is irrelevant :( I just can't figure out how the potentially harmful in code declarations are still here, but the potentially useful in code definitions are not. I've given you a +1 for the beneficial examples.Spada
R
2

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...
Raceme answered 30/4, 2015 at 13:11 Comment(5)
Obviously this behavior is defined for lambdas? Why not functions defined in code?Spada
A lambda is merely shorthand for writing a function object. The special case of a lambda that captures no arguments is equivalent to a local function definition, as is writing a function object that has no data members.Raceme
I was just pointing out that lambdas, and in code declared functions already dismiss all your points. It should be no increase in "burden".Spada
@JonathanMee if you feel strongly about it, by all means submit an RFC to the c++ standards committee.Raceme
Shafik Yaghmour's Answer covered that already being done. I personally would like to see the removal of the ability to declare functions in code if they won't let us define them. Richard Hodges's answer does a good job of explaining why we still need the ability to declare in code declaration though.Spada
C
1

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 lambdas to C++, this question is a bit obsolete now.


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.

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.

Cleavage answered 19/4, 2016 at 21:49 Comment(1)
I understood that we could define a lambda inline. I understood that we could declare a function inline, but that's the origin of the most vexing parse, so my question was if the standard is going to keep functionality that serves only to induce rage in programmers, shouldn't programmers be able to define the function inline too? Richard Hodges' answer helped me understand the origin of this problem.Spada
V
0

Nested function declarations are allowed probably for 1. Forward references 2. To be able to declare a pointer to function(s) and pass around other function(s) in a limited scope.

Nested function definitions are not allowed probably due to issues like 1. Optimization 2. Recursion (enclosing and nested defined function(s)) 3. Re-entrancy 4. Concurrency and other multithread access issues.

From my limited understanding :)

Vegetal answered 28/6, 2017 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.