Programming

C ++에서 함수 내부에 함수를 가질 수 있습니까?

procodes 2020. 5. 17. 19:39
반응형

C ++에서 함수 내부에 함수를 가질 수 있습니까?


나는 다음과 같은 것을 의미한다 :

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

  return 0;
}

Modern C ++-람다가 있습니다!

현재 버전의 c ++ (C ++ 11, C ++ 14 및 C ++ 17)에서는 함수 내부에 람다 형식으로 함수를 사용할 수 있습니다.

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는 ** 기준 별 캡처 *를 통해 로컬 변수를 수정할 수도 있습니다. 참조 별 캡처를 사용하면 람다는 람다 범위에서 선언 된 모든 지역 변수에 액세스 할 수 있습니다. 정상적으로 수정하고 변경할 수 있습니다.

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 및 C ++ 03-직접은 아니지만 로컬 클래스 내부의 정적 함수 사용

C ++은이를 직접 지원하지 않습니다.

즉, 로컬 클래스를 가질 수 있고 함수 ( static또는 static)를 가질 수 있으므로 약간의 혼란이 있지만 확장 할 수 있습니다.

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

  X::a();

  return 0;
}

그러나 나는 연습에 의문을 제기했다. 모든 사람은 (물론, 당신이 지금, 어쨌든 알고 :)그들은 그것들을 가지고 있지에 사용되도록) C ++의 지역 기능을 지원하지 않습니다. 그러나 그들은 그 kludge에 사용되지 않습니다. 이 코드에서 로컬 기능을 허용하기 위해 실제로 만 존재하는지 확인하는 데 꽤 오랜 시간을 소비했습니다. 안좋다.


모든 의도와 목적을 위해 C ++은 람다 를 통해이를 지원합니다 . 1

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

여기에 f의 로컬 함수 역할을하는 람다 객체가 main있습니다. 함수가 로컬 오브젝트에 액세스 할 수 있도록 캡처를 지정할 수 있습니다.

내부적 fA는 함수 객체 (AN을 제공하는 타입의 객체 즉, operator()). 함수 객체 유형은 람다를 기반으로 컴파일러에서 생성합니다.


C ++ 11 이후 1


로컬 클래스는 이미 언급되었지만 다음은 operator () 오버로드와 익명 클래스를 사용하여 로컬 함수로 표시하는 방법입니다.

int main() {
    struct {
        unsigned int operator() (unsigned int val) const {
            return val<=1 ? 1 : val*(*this)(val-1);
        }
    } fac;

    std::cout << fac(5) << '\n';
}

나는 이것을 사용하는 것에 대해 조언하지 않지만, 그것은 단지 재미있는 트릭입니다 (할 수는 있지만 imho는해서는 안됩니다).


2014 년 업데이트 :

C ++ 11이 등장하면서 JavaScript를 약간 연상시키는 구문을 가진 로컬 함수를 사용할 수 있습니다.

auto fac = [] (unsigned int val) {
    return val*42;
};

아니.

무엇을하려고합니까?

해결 방법 :

int main(void)
{
  struct foo
  {
    void operator()() { int a = 1; }
  };

  foo b;
  b(); // call the operator()

}

오래된 대답 : 당신은 일종의 할 수는 있지만 더미 클래스를 속이고 사용해야합니다.

void moo()
{
    class dummy
    {
    public:
         static void a() { printf("I'm in a!\n"); }
    };

    dummy::a();
    dummy::a();
}

최신 답변 : 최신 버전의 C ++은 람다를 지원하여 더 잘 / 적절하게 수행합니다. 페이지 상단의 답변을 참조하십시오.


아니요, 허용되지 않습니다. C 나 C ++ 모두 기본적으로이 기능을 지원하지는 않지만 TonyK는 주석에서 C에서이 동작을 가능하게하는 GNU C 컴파일러의 확장이 있다고 지적합니다.


다른 사람들이 언급했듯이 gcc에서 gnu 언어 확장을 사용하여 중첩 함수를 사용할 수 있습니다. 당신 (또는 당신의 프로젝트)이 gcc 툴체인을 고수한다면, 코드는 대부분 gcc 컴파일러가 목표로하는 다양한 아키텍처에서 이식 가능합니다.

그러나 다른 툴체인으로 코드를 컴파일해야 할 수있는 요구 사항이있을 경우 그러한 확장을 피할 수 있습니다.


또한 중첩 함수를 사용할 때주의를 기울였습니다. 복잡하지만 응집력있는 코드 블록 (외부 / 일반적인 용도로 사용되지 않는 코드)의 구조를 관리하기위한 아름다운 솔루션입니다. 또한 네임 스페이스 오염을 제어하는 ​​데 매우 유용합니다 (자연적으로 복잡한 / 장황한 언어로 된 긴 수업.)

그러나 무엇이든 마찬가지로 학대에 노출 될 수 있습니다.

C / C ++가 표준과 같은 기능을 지원하지 않는 것은 슬픈 일입니다. 대부분의 파스칼 변형과 Ada는 거의 모든 Algol 기반 언어를 지원합니다. JavaScript와 동일합니다. 스칼라와 같은 현대 언어와 동일합니다. Erlang, Lisp 또는 Python과 같은 훌륭한 언어와 동일합니다.

그리고 C / C ++과 마찬가지로 불행히도 Java (내 삶의 대부분을 얻음)는 그렇지 않습니다.

여기에서는 중첩 함수의 대안으로 클래스 및 클래스의 메소드 사용을 제안하는 여러 포스터가 있기 때문에 Java를 언급합니다. 그리고 이것은 Java의 일반적인 해결 방법이기도합니다.

짧은 대답 : 아니요.

그렇게하면 클래스 계층 구조에서 인공적이고 불필요하게 복잡해집니다. 모든 것이 동일하기 때문에 실제 도메인을 가능한 한 간단하게 나타내는 클래스 계층 구조 (및 포함 네임 스페이스 및 범위)를 갖는 것이 이상적입니다.

중첩 된 함수는 "개인"기능 내 복잡성을 처리하는 데 도움이됩니다. 이러한 시설이 없기 때문에 "개인"복잡성을 자신의 클래스 모델로 전파하지 않도록 노력해야합니다.

소프트웨어 (및 모든 엔지니어링 분야)에서 모델링은 절충의 문제입니다. 따라서 실제로는 이러한 규칙 (또는 지침)에 대한 예외가 정당화됩니다. 그래도 조심해서 진행하십시오.


C ++에서 다른 함수 안에서 자유 함수를 정의 할 수 없습니다.


C ++에는 로컬 함수를 사용할 수 없습니다. 그러나 C ++ 11에는 lambdas가 있습니다. 람다는 기본적으로 함수처럼 작동하는 변수입니다.

람다는 유형이 있습니다 std::function( 실제로 사실 은 아니지만 대부분의 경우 유형 이라고 가정 할 수 있음). 이 유형을 사용하려면을 수행해야합니다 #include <functional>. std::function템플릿으로, 반환 형식과 인수 형식을 템플릿 인수로하여 구문을 사용합니다 std::function<ReturnType(ArgumentTypes). 예를 들어,을 std::function<int(std::string, float)>반환 하고 하나의 int인수 std::string와 하나의 인수를 취하는 람다 float입니다. 가장 일반적인 것은이며 std::function<void()>, 아무것도 반환하지 않으며 인수를 사용하지 않습니다.

람다가 선언되면 구문을 사용하여 일반 함수처럼 호출 lambda(arguments)됩니다.

람다를 정의하려면 구문을 사용하십시오 [captures](arguments){code}(다른 방법은 있지만 여기서는 언급하지 않습니다). arguments람다가 취하는 인수이며 code람다가 호출 될 때 실행되어야하는 코드입니다. 일반적으로 [=]또는 [&]캡처로 넣습니다 . [=]값이 값으로 정의되는 범위에서 모든 변수를 캡처한다는 것을 의미합니다. 즉, 람다가 선언되었을 때의 값을 유지합니다. [&]즉, 범위의 모든 변수를 참조로 캡처한다는 의미입니다. 즉, 항상 현재 값을 가지지 만 메모리에서 지워지면 프로그램이 중단됩니다. 여기 몇 가지 예가 있어요.

#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 value 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;
}

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.


Let me post a solution here for C++03 that I consider the cleanest possible.*

#define DECLARE_LAMBDA(NAME, RETURN_TYPE, FUNCTION) \
    struct { RETURN_TYPE operator () FUNCTION } NAME;

...

int main(){
  DECLARE_LAMBDA(demoLambda, void, (){ cout<<"I'm a lambda!"<<endl; });
  demoLambda();

  DECLARE_LAMBDA(plus, int, (int i, int j){
    return i+j;
  });
  cout << "plus(1,2)=" << plus(1,2) << endl;
  return 0;
}

(*) in the C++ world using macros is never considered clean.


But we can declare a function inside main():

int main()
{
    void a();
}

Although the syntax is correct, sometimes it can lead to the "Most vexing parse":

#include <iostream>


struct U
{
    U() : val(0) {}
    U(int val) : val(val) {}

    int val;
};

struct V
{
    V(U a, U b)
    {
        std::cout << "V(" << a.val << ", " << b.val << ");\n";
    }
    ~V()
    {
        std::cout << "~V();\n";
    }
};

int main()
{
    int five = 5;
    V v(U(five), U());
}

=> no program output.

(Only Clang warning after compilation).

C++'s most vexing parse again


When you try to implement a function in another function body, you have to get this error as Illegal definition :

error C2601: 'a' : local function definitions are illegal
IntelliSense: expected a ';'

So don't try it again.

참고URL : https://stackoverflow.com/questions/4324763/can-we-have-functions-inside-functions-in-c

반응형