我们可以在C ++中的函数内部使用函数吗?

时间:2010-12-01 13:23:27

标签: c++

我的意思是:

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

  return 0;
}

13 个答案:

答案 0 :(得分:245)

出于所有意图和目的,C ++通过lambdas支持这一点: 1

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

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

在幕后,ffunction object(即提供operator()的类型的对象)。函数对象类型由编译器基于lambda创建。


1 ,因为C ++ 11

答案 1 :(得分:179)

现代C ++ - 是的,有lambdas!

在当前版本的c ++(C ++ 11,C ++ 14和C ++ 17)中,您可以以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还可以通过** capture-by-reference *修改局部变量。通过引用捕获,lambda可以访问lambda范围内声明的所有局部变量。它可以正常修改和更改它们。

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 ++不直接支持。

那就是说,你可以拥有本地类,并且它们可以有函数(非staticstatic),所以你可以在一定程度上得到这个,尽管它有点像kludge:

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

  X::a();

  return 0;
}

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

答案 2 :(得分:39)

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

答案 3 :(得分:15)

没有

你想做什么?

解决方法:

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

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

}

答案 4 :(得分:12)

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

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

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

更新的答案:较新版本的C ++也支持lambdas更好/正确地执行此操作。查看页面上方的答案。

答案 5 :(得分:7)

不,这是不允许的。默认情况下,C和C ++都不支持此功能,但TonyK指出(在注释中)GNU C编译器有扩展,可以在C中启用此行为。

答案 6 :(得分:7)

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

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


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

但与任何事情一样,他们可能会被滥用。

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

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

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

简答:否。

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

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

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

答案 7 :(得分:6)

您无法在C ++中定义另一个自由函数。

答案 8 :(得分:5)

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

答案 9 :(得分:5)

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

lambda的类型为std::functionactually that's not quite true,但在大多数情况下,您可以认为它是)。要使用此类型,您需要#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}(还有其他方法可以做到,但我在这里没有提到它们)。 arguments是lambda采用的参数,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;
}

您还可以通过指定名称来捕获特定变量。只需指定其名称将按值捕获它们,使用&指定其名称之前将通过引用捕获它们。例如,[=, &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;
}

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

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

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

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

<强>实施例

以下是使用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;
}

这是一个更高级的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;
}

答案 10 :(得分:3)

让我在这里为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;
}
使用宏的C ++世界中的

(*)从未被认为是干净的。

答案 11 :(得分:2)

但我们可以在main()中声明一个函数:

int main()
{
    void a();
}

虽然语法是正确的,但有时它可能导致“最令人烦恼的解析”:

#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());
}

=&GT;没有节目输出。

(编译后只有Clang警告。)

C++'s most vexing parse again

答案 12 :(得分:0)

当您尝试在另一个函数体中实现一个函数时,必须将此error作为非法定义:

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

所以不要再试一次。