我的意思是这样的:
int main()
{
void a()
{
// code
}
a();
return 0;
}
我的意思是这样的:
int main()
{
void a()
{
// code
}
a();
return 0;
}
在当前的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!");
}
}
通过**按引用捕获*,Lambda表达式也可以修改本地变量。使用按引用捕获方式,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++不直接支持这个功能。
尽管如此,您可以使用局部类,并且它们可以具有函数(非static
或static
),因此您可以在某种程度上实现此目标,尽管这有些笨拙:
int main() // it's int, dammit!
{
struct X { // struct's as good as class
static void a()
{
}
};
X::a();
return 0;
}
然而,我对这种做法持怀疑态度。每个人都知道(嗯,现在你也知道了:)
),C++不支持局部函数,所以他们习惯于没有它们。然而,他们不习惯于那个补救措施。我会花很长时间在这段代码上,确保它只是为了允许局部函数而存在。这不好。
int main()
和 int main(int argc, char* argv[])
。 - John Diblingint main()
和int main(int argc, char* argv[])
,其他可能会被支持,但所有的都必须有返回值 int。 - JoeG已经提到了本地类,但以下是一种让它们看起来更像本地函数的方法,使用一个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';
}
我不建议使用这个,它只是一个有趣的技巧(可以做,但我认为不应该)。
随着C++11的崛起,你现在可以拥有本地函数,其语法有点类似于JavaScript:
auto fac = [] (unsigned int val) {
return val*42;
};
对于递归函数,不支持编译时类型推导:
function<int(int)> factorial{ [&](int n)
{
return (n == 1 || n == 0) ? 1 : factorial(n - 1) * n;
} };
operator () (unsigned int val)
,你少了一对括号。 - Joe Dstd::sort()
或std::for_each()
),那么这是完全合理的事情。 - Dimaauto
来声明变量。Stroustrup给出了一个示例:function<void(char*b, char*e)> rev=[](char*b, char*e) { if( 1<e-b ) { swap( *b, *--e); rev(++b,e); } };
,用于翻转给定起始和结束指针的字符串。 - EponymousLambda表达式具有类型std::function
(实际上并不完全正确, 但在大多数情况下可以假设它是这样的)。要使用此类型,您需要#include <functional>
。 std::function
是一个模板,以返回类型和参数类型为模板参数,语法为std::function<ReturnType(ArgumentTypes)>
。例如,std::function<int(std::string, float)>
是一个返回int
并接受两个参数(一个std::string
和一个float
)的lambda表达式。最常见的是std::function<void()>
,它不返回任何东西并且不带参数。
一旦声明了lambda表达式,就可以像普通函数一样调用它,使用语法lambda(arguments)
。
[captures](arguments){code}
(还有其他方法,但我不会在这里提到)。arguments
是 lambda 表达式的参数,code
是调用 lambda 时要运行的代码。通常,您可以使用 [=]
或 [&]
作为捕获方式。 [=]
表示按值捕获上下文中定义的所有变量的值,这意味着它们将保留在声明 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 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;
}
&
将按引用捕获它们。例如,[=,&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内部通过值捕获的变量的值(通过值捕获的变量在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;
}
std::function<void()> lambda;
lambda(); //Undefined behavior because lambda is uninitialized
例子
这是使用lambda表达式实现你在问题中想要做的代码:
#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;
}
不行。
你正在尝试做什么?
解决方法:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
foo
是trivially default-constructible,尽管没有成员,但它并不具有零大小,因为标准不允许零大小的结构类型,但是除非你将从堆栈指针寄存器中减去一个常数视为“分配”(无论如何,这将被任何明智的编译器在-O1
级别上消除),否则它是免费的。我不是说它不丑陋;我的观点是它是没有分配的。 :) - kkmfoo::f()
。无论如何,我还是点了赞,因为这绝对是在 C++03 中实现它的最直接的方法。 - Keith Russell从C++11开始,您可以使用适当的lambda表达式。有关更多详细信息,请参阅其他答案。
旧的答案:您可以使用一个虚假类,但需要进行欺骗:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
不可以。C和C++默认都不支持该功能,但是TonyK在评论中指出,GNU C编译器有扩展可以在C语言中实现这种行为。
在C++中,您无法在另一个函数内定义自由函数。
正如其他人所提到的,您可以使用gcc中的gnu语言扩展来使用嵌套函数。如果您(或您的项目)坚持使用gcc工具链,则您的代码将在gcc编译器针对的不同体系结构上大多数可移植。
但是,如果可能需要使用不同的工具链编译代码,则我会避免使用此类扩展。
在使用嵌套函数时,也要小心。它们是管理复杂而紧密代码块结构的绝佳解决方案(这些代码块的各个部分并不用于外部/通用用途)。它们还非常有助于控制命名空间污染(在冗长的语言和自然复杂/长类的情况下,这是一个非常现实的问题)。
但同样,它们也可能被滥用。
很遗憾,C/C++不支持此类特性作为标准。大多数Pascal变体和Ada都支持(几乎所有Algol-based语言都支持)。JavaScript也是一样。现代语言Scala也是如此。尊贵的语言Erlang、Lisp或Python也是如此。
就像C/C++一样,不幸的是,Java(我赚取大部分收入的语言)也不支持。
我在这里提到Java,因为我看到有几个海报建议使用类和类的方法作为嵌套函数的替代方案。这也是Java中的典型解决方法。
简短的回答:不行。
这样做往往会在类层次结构上引入人为和不必要的复杂性。所有事情都相等的情况下,理想情况是使表示实际领域的类层次结构(及其包含的命名空间和范围)尽可能简单。
嵌套函数有助于处理“私有”的内部功能。如果缺少这些设施,则应尝试避免将该“私有”复杂性传播到类模型中。
在软件(以及任何工程学科)中,建模是一种权衡。因此,在现实生活中,将有合理的例外情况(或者更确切地说是指南)。但是要小心。
所有这些技巧看起来(或多或少)像本地函数,但它们并不像本地函数那样工作。在本地函数中,您可以使用其超级函数的局部变量。它是一种半全局变量。这些技巧中没有一个能做到这一点。最接近的是c++0x中的lambda技巧,但它的闭包在定义时间绑定,而不是使用时间。