我们可以在函数内部有函数吗?

Rel*_*lla 189 c++

我的意思是:

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

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Kon*_*lph 254

对于所有意图和目的,C++通过lambdas支持:1

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

这里f是一个lambda对象,它充当了一个本地函数main.可以指定Captures以允许函数访问本地对象.

在幕后,f是一个功能对象(即提供的类型的对象operator()).函数对象类型由编译器基于lambda创建.


1,自C++ 11以来

  • 啊,那很整洁!我没想到.这比我的想法要好得多,"+ 1"来自我. (4认同)
  • @luis:我必须同意弗雷德.你附加了一个他们根本没有的lambda的含义(无论是在C++中还是在我曾经使用过的其他语言中 - **不包括Python和Ada,用于记录).此外,在C++中进行这种区分只是没有意义,因为C++没有本地函数,句点.它只有lambdas.如果要将类函数的范围限制为函数,则唯一的选择是lambdas或其他答案中提到的本地结构.我会说后者太复杂而没有任何实际意义. (3认同)
  • @AustinWBryan不,C ++中的lambda只是函子的语法糖,并且开销为零。这个网站上的某个地方有更详细的问题。 (2认同)

sbi*_*sbi 218

不,C++不支持这一点.

编辑:这个答案很老.同时,C++ 11有lambda可以达到类似的结果 - 见下面的答案.

也就是说,你可以拥有本地类,并且它们可以具有函数(非static或者static),所以你可以在某种程度上得到它,尽管它有点像kludge:

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!"); 
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我会质疑实践.每个人都知道(好吧,无论如何你现在都这样做:))C++不支持本地函数,所以他们习惯没有它们.但是,它们并没有被用于那个kludge.我会在这段代码上花一点时间来确保它只是允许本地函数.不好.

  • @Thomas Owens:如果你需要一个回调函数并且不想用它来污染其他一些命名空间,那就太好了. (16认同)
  • @Leo:标准说main有两种允许的形式:`int main()`和`int main(int argc,char*argv [])` (9认同)
  • 标准说`int main()`和`int main(int argc,char*argv [])`必须得到支持,其他人可能会被支持,但它们都返回int. (8认同)
  • 如果你想对返回类型迂腐,主要还需要两个参数.:)(或者这是可选的,但这些天不是回报?我无法跟上.) (3认同)
  • 这很糟糕 - 它打破了良好,干净的代码的每一个惯例.我想不出这是一个好主意的单一实例. (3认同)
  • 它在模板定义中很有用 - 它们必须放在头文件中,因此使用本地函数是避免命名空间污染的唯一方法. (3认同)
  • @Kabumbus:你不能。这就是为什么它被称为_workaround_。如果语法流畅,则它会被称为您的问题的 _solution_。 (2认同)

Seb*_*ach 41

已经提到了本地类,但是这里有一种方法可以让它们更像本地函数,使用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';
}
Run Code Online (Sandbox Code Playgroud)

我不建议使用它,这只是一个有趣的技巧(可以做,但imho不应该).


2014年更新:

随着C++ 11的兴起,您现在可以拥有本地函数,其语法有点像JavaScript的回忆:

auto fac = [] (unsigned int val) {
    return val*42;
};
Run Code Online (Sandbox Code Playgroud)

  • 支持递归。但是,不能使用```auto```来声明变量。Stroustrup给出了示例:```function &lt;void(char * b,char * e)&gt; rev = [](char * b,char * e){if(1 &lt;eb){swap(* b,*- e); rev(++ b,e); }};```用于反转给定开始和结束指针的字符串。 (2认同)

Nim*_*Nim 17

没有.

你想做什么?

解决方法:

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

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

}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,类实例化方法随内存分配一起提供,因此由静态方法主导。 (2认同)

Leo*_*son 14

旧答案:你可以,但是你必须作弊并使用虚拟课:

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

    dummy::a();
    dummy::a();
}
Run Code Online (Sandbox Code Playgroud)

更新的答案:较新版本的C++也支持lambdas更好/正确地做到这一点.查看页面上方的答案.


lui*_*nal 8

正如其他人所提到的,您可以通过在gcc中使用gnu语言扩展来使用嵌套函数.如果您(或您的项目)坚持使用gcc工具链,您的代码将基本上可以在gcc编译器所针对的不同体系结构中移植.

但是,如果可能需要使用不同的工具链编译代码,那么我将远离这些扩展.


使用嵌套函数时,我也会小心翼翼.它们是一个很好的解决方案,用于管理复杂但有凝聚力的代码块(其中的部分不适用于外部/一般用途).它们在控制命名空间污染方面也非常有用(对于自然复杂的/非常真实的关注/冗长的语言.)

但就像任何事情一样,他们可能会被滥用.

令人遗憾的是,C/C++不支持标准等功能.大多数pascal变体和Ada do(几乎所有基于Algol的语言都有).与JavaScript相同.与Scala等现代语言相同.与Erlang,Lisp或Python等古老语言相同.

就像使用C/C++一样,不幸的是,Java(我的大部分时间都是我的生活)并没有.

我在这里提到Java,因为我看到几个海报建议使用类和类'方法作为嵌套函数的替代.这也是Java中的典型解决方法.

简答:不.

这样做往往会在类层次结构中引入人为的,不必要的复杂性.在所有条件相同的情况下,理想的是使类层次结构(及其包含的名称空间和范围)尽可能简单地表示实际域.

嵌套函数有助于处理"私有",函数内复杂性.缺乏这些设施,人们应该尽量避免将"私人"复杂性传播到一个人的阶级模型中.

在软件(以及任何工程学科)中,建模是一个权衡问题.因此,在现实生活中,这些规则(或指导方针)将有合理的例外.但是要小心.


Don*_*uck 8

您不能在C++中使用本地函数.但是,C++ 11有lambdas.Lambdas基本上是像函数一样工作的变量.

一个lambda有这种类型std::function(实际上这不是真的,但在大多数情况下,你可以认为它是).要使用此类型,您需要#include <functional>.std::function是一个模板,使用语法作为返回类型和参数类型的模板参数std::function<ReturnType(ArgumentTypes).例如,std::function<int(std::string, float)>是一个lambda返回并带有int两个参数,一个std::string和一个float.最常见的是std::function<void()>,它不返回任何内容并且不带参数.

一旦声明了lambda,就会使用语法将其调用为普通函数lambda(arguments).

要定义lambda,请使用语法[captures](arguments){code}(还有其他方法,但我不会在这里提到它们).argumentslambda是什么参数,code是调用lambda时应该运行的代码.通常你放[=][&]作为捕获.[=]表示您捕获范围中的所有变量,其中值由value定义,这意味着它们将保留声明lambda时的值.[&]表示您通过引用捕获作用域中的所有变量,这意味着它们将始终具有其当前值,但如果它们从内存中擦除,则程序将崩溃.这里有些例子:

#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;
}
Run Code Online (Sandbox Code Playgroud)

您还可以通过指定名称来捕获特定变量.只需指定其名称将按值捕获它们,使用&before 指定其名称将通过引用捕获它们.例如,[=, &foo]将按值捕获所有变量,除非foo将通过引用[&, foo]捕获,并将通过引用捕获所有变量,除非foo将通过值捕获.您还可以仅捕获特定变量,例如[&foo]foo通过引用捕获并且不捕获任何其他变量.您也可以通过使用来捕获任何变量[].如果您尝试在未捕获的lambda中使用变量,则无法编译.这是一个例子:

#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;
}
Run Code Online (Sandbox Code Playgroud)

您无法更改由lambda中的值捕获的变量的值(由value捕获的变量const在lambda中具有类型).为此,您需要通过引用捕获变量.这是一个例子:

#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;
}
Run Code Online (Sandbox Code Playgroud)

此外,调用未初始化的lambda是未定义的行为,通常会导致程序崩溃.例如,永远不要这样做:

std::function<void()> lambda;
lambda();    //Undefined behavior because lambda is uninitialized
Run Code Online (Sandbox Code Playgroud)

例子

以下是使用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;
}
Run Code Online (Sandbox Code Playgroud)

这是一个更高级的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;
}
Run Code Online (Sandbox Code Playgroud)


Tho*_*ens 7

不,这是不允许的.无论ç也不是C++支持默认情况下此功能,但是TonyK指出(在评论),有扩展的GNU C编译器,使在C.这种行为

  • GNU C编译器支持它作为特殊扩展.但只适用于C,而不是C++. (2认同)

小智 7

所有这些技巧只是看(或多或少)作为本地函数,但它们不会那样工作.在本地函数中,您可以使用其超函数的局部变量.这是半全局的.没有这些技巧可以做到这一点.最接近的是来自c ++ 0x的lambda技巧,但是它的闭包是在定义时间中绑定的,而不是使用时间.


Pra*_*rav 6

您无法在C++中的另一个内部定义自由函数.

  • 不能使用 ansi/posix,但可以使用 gnu 扩展。 (2认同)

X''*_*X'' 5

让我在这里发布一个我认为最干净的 C++03 解决方案。*

#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;
}
Run Code Online (Sandbox Code Playgroud)

(*) 在 C++ 世界中使用宏从来不被认为是干净的。