Can we have functions inside functions in C++?

后端 未结 12 2159
抹茶落季
抹茶落季 2020-11-22 14:51

I mean something like:

int main() 
{
  void a() 
  {
      // code
  }
  a();

  return 0;
}
相关标签:
12条回答
  • 2020-11-22 15:38

    Modern C++ - Yes with lambdas!

    In current versions of c++ (C++11, C++14, and C++17), you can have functions inside functions in the form of a lambda:

    int main() {
        // This declares a lambda, which can be called just like a function
        auto print_message = [](std::string message) 
        { 
            std::cout << message << "\n"; 
        };
    
        // Prints "Hello!" 10 times
        for(int i = 0; i < 10; i++) {
            print_message("Hello!"); 
        }
    }
    

    Lambdas can also modify local variables through **capture-by-reference*. With capture-by-reference, the lambda has access to all local variables declared in the lambda's scope. It can modify and change them normally.

    int main() {
        int i = 0;
        // Captures i by reference; increments it by one
        auto addOne = [&] () {
            i++; 
        };
    
        while(i < 10) {
            addOne(); //Add 1 to i
            std::cout << i << "\n";
        }
    }
    

    C++98 and C++03 - Not directly, but yes with static functions inside local classes

    C++ doesn't support that directly.

    That said, you can have local classes, and they can have functions (non-static or static), so you can get this to some extend, albeit it's a bit of a kludge:

    int main() // it's int, dammit!
    {
      struct X { // struct's as good as class
        static void a()
        {
        }
      };
    
      X::a();
    
      return 0;
    }
    

    However, I'd question the praxis. Everyone knows (well, now that you do, anyway :)) C++ doesn't support local functions, so they are used to not having them. They are not used, however, to that kludge. I would spend quite a while on this code to make sure it's really only there to allow local functions. Not good.

    0 讨论(0)
  • 2020-11-22 15:44

    All this tricks just look (more or less) as local functions, but they don't work like that. In a local function you can use local variables of it's super functions. It's kind of semi-globals. Non of these tricks can do that. The closest is the lambda trick from c++0x, but it's closure is bound in definition time, not the use time.

    0 讨论(0)
  • 2020-11-22 15:45

    For all intents and purposes, C++ supports this via lambdas:1

    int main() {
        auto f = []() { return 42; };
        std::cout << "f() = " << f() << std::endl;
    }
    

    Here, f is a lambda object that acts as a local function in main. Captures can be specified to allow the function to access local objects.

    Behind the scenes, f is a function object (i.e. an object of a type that provides an operator()). The function object type is created by the compiler based on the lambda.


    1 since C++11

    0 讨论(0)
  • 2020-11-22 15:45

    You cannot define a free function inside another in C++.

    0 讨论(0)
  • 2020-11-22 15:46

    You can't have local functions in C++. However, C++11 has lambdas. Lambdas are basically variables that work like functions.

    A lambda has the type std::function (actually that's not quite true, but in most cases you can suppose it is). To use this type, you need to #include <functional>. std::function is a template, taking as template argument the return type and the argument types, with the syntax std::function<ReturnType(ArgumentTypes)>. For example, std::function<int(std::string, float)> is a lambda returning an int and taking two arguments, one std::string and one float. The most common one is std::function<void()>, which returns nothing and takes no arguments.

    Once a lambda is declared, it is called just like a normal function, using the syntax lambda(arguments).

    To define a lambda, use the syntax [captures](arguments){code} (there are other ways of doing it, but I won't mention them here). arguments is what arguments the lambda takes, and code is the code that should be run when the lambda is called. Usually you put [=] or [&] as captures. [=] means that you capture all variables in the scope in which the value is defined by value, which means that they will keep the value that they had when the lambda was declared. [&] means that you capture all variables in the scope by reference, which means that they will always have their current value, but if they are erased from memory the program will crash. Here are some examples:

    #include <functional>
    #include <iostream>
    
    int main(){
        int x = 1;
    
        std::function<void()> lambda1 = [=](){
            std::cout << x << std::endl;
        };
        std::function<void()> lambda2 = [&](){
            std::cout << x << std::endl;
        };
    
        x = 2;
        lambda1();    //Prints 1 since that was the value of x when it was captured and x was captured by value with [=]
        lambda2();    //Prints 2 since that's the current value of x and x was captured by reference with [&]
    
        std::function<void()> lambda3 = [](){}, lambda4 = [](){};    //I prefer to initialize these since calling an uninitialized lambda is undefined behavior.
                                                                     //[](){} is the empty lambda.
    
        {
            int y = 3;    //y will be deleted from the memory at the end of this scope
            lambda3 = [=](){
                std::cout << y << endl;
            };
            lambda4 = [&](){
                std::cout << y << endl;
            };
        }
    
        lambda3();    //Prints 3, since that's the value y had when it was captured
    
        lambda4();    //Causes the program to crash, since y was captured by reference and y doesn't exist anymore.
                      //This is a bit like if you had a pointer to y which now points nowhere because y has been deleted from the memory.
                      //This is why you should be careful when capturing by reference.
    
        return 0;
    }
    

    You can also capture specific variables by specifying their names. Just specifying their name will capture them by value, specifying their name with a & before will capture them by reference. For example, [=, &foo] will capture all variables by value except foo which will be captured by reference, and [&, foo] will capture all variables by reference except foo which will be captured by value. You can also capture only specific variables, for example [&foo] will capture foo by reference and will capture no other variables. You can also capture no variables at all by using []. If you try to use a variable in a lambda that you didn't capture, it won't compile. Here is an example:

    #include <functional>
    
    int main(){
        int x = 4, y = 5;
    
        std::function<void(int)> myLambda = [y](int z){
            int xSquare = x * x;    //Compiler error because x wasn't captured
            int ySquare = y * y;    //OK because y was captured
            int zSquare = z * z;    //OK because z is an argument of the lambda
        };
    
        return 0;
    }
    

    You can't change the value of a variable that was captured by value inside a lambda (variables captured by value have a const type inside the lambda). To do so, you need to capture the variable by reference. Here is an exampmle:

    #include <functional>
    
    int main(){
        int x = 3, y = 5;
        std::function<void()> myLambda = [x, &y](){
            x = 2;    //Compiler error because x is captured by value and so it's of type const int inside the lambda
            y = 2;    //OK because y is captured by reference
        };
        x = 2;    //This is of course OK because we're not inside the lambda
        return 0;
    }
    

    Also, calling uninitialized lambdas is undefined behavior and will usually cause the program to crash. For example, never do this:

    std::function<void()> lambda;
    lambda();    //Undefined behavior because lambda is uninitialized
    

    Examples

    Here is the code for what you wanted to do in your question using lambdas:

    #include <functional>    //Don't forget this, otherwise you won't be able to use the std::function type
    
    int main(){
        std::function<void()> a = [](){
            // code
        }
        a();
        return 0;
    }
    

    Here is a more advanced example of a lambda:

    #include <functional>    //For std::function
    #include <iostream>      //For std::cout
    
    int main(){
        int x = 4;
        std::function<float(int)> divideByX = [x](int y){
            return (float)y / (float)x;    //x is a captured variable, y is an argument
        }
        std::cout << divideByX(3) << std::endl;    //Prints 0.75
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-22 15:46

    As others have mentioned, you can use nested functions by using the gnu language extensions in gcc. If you (or your project) sticks to the gcc toolchain, your code will be mostly portable across the different architectures targeted by the gcc compiler.

    However, if there is a possible requirement that you might need to compile code with a different toolchain, then I'd stay away from such extensions.


    I'd also tread with care when using nested functions. They are a beautiful solution for managing the structure of complex, yet cohesive blocks of code (the pieces of which are not meant for external/general use.) They are also very helpful in controlling namespace pollution (a very real concern with naturally complex/long classes in verbose languages.)

    But like anything, they can be open to abuse.

    It is sad that C/C++ does not support such features as an standard. Most pascal variants and Ada do (almost all Algol-based languages do). Same with JavaScript. Same with modern languages like Scala. Same with venerable languages like Erlang, Lisp or Python.

    And just as with C/C++, unfortunately, Java (with which I earn most of my living) does not.

    I mention Java here because I see several posters suggesting usage of classes and class' methods as alternatives to nested functions. And that's also the typical workaround in Java.

    Short answer: No.

    Doing so tend to introduce artificial, needless complexity on a class hierarchy. With all things being equal, the ideal is to have a class hierarchy (and its encompassing namespaces and scopes) representing an actual domain as simple as possible.

    Nested functions help deal with "private", within-function complexity. Lacking those facilities, one should try to avoid propagating that "private" complexity out and into one's class model.

    In software (and in any engineering discipline), modeling is a matter of trade-offs. Thus, in real life, there will be justified exceptions to those rules (or rather guidelines). Proceed with care, though.

    0 讨论(0)
提交回复
热议问题