Ý tôi là đại loại như:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Ý tôi là đại loại như:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Câu trả lời:
Trong các phiên bản hiện tại của c ++ (C ++ 11, C ++ 14 và C ++ 17), bạn có thể có các chức năng bên trong các chức năng dưới dạng 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 cũng có thể sửa đổi các biến cục bộ thông qua ** Capture-by-Reference *. Với tham chiếu bắt giữ, lambda có quyền truy cập vào tất cả các biến cục bộ được khai báo trong phạm vi của lambda. Nó có thể sửa đổi và thay đổi chúng bình thường.
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 ++ không hỗ trợ trực tiếp.
Điều đó nói rằng, bạn có thể có các lớp cục bộ và chúng có thể có các hàm (không static
hoặc static
), vì vậy bạn có thể đưa nó đến một số phần mở rộng, mặc dù đó là một chút của một loại bùn:
int main() // it's int, dammit!
{
struct X { // struct's as good as class
static void a()
{
}
};
X::a();
return 0;
}
Tuy nhiên, tôi sẽ hỏi những lời khen ngợi. Mọi người đều biết (dù sao thì bây giờ bạn cũng vậy :)
) C ++ không hỗ trợ các chức năng cục bộ, vì vậy họ đã quen với việc không có chúng. Chúng không được sử dụng, tuy nhiên, để loại bỏ bùn đó. Tôi sẽ dành khá nhiều thời gian cho mã này để đảm bảo rằng nó thực sự chỉ ở đó để cho phép các chức năng cục bộ. Không tốt.
int main()
vàint main(int argc, char* argv[])
int main()
và int main(int argc, char* argv[])
phải được hỗ trợ và những người khác có thể được hỗ trợ nhưng tất cả họ đều có int.
Đối với tất cả ý định và mục đích, C ++ hỗ trợ điều này thông qua lambdas : 1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
Ở đây, f
là một đối tượng lambda hoạt động như một hàm cục bộ main
. Chụp có thể được chỉ định để cho phép chức năng truy cập các đối tượng cục bộ.
Đằng sau hậu trường, f
là một đối tượng chức năng (tức là một đối tượng thuộc loại cung cấp một operator()
). Kiểu đối tượng hàm được tạo bởi trình biên dịch dựa trên lambda.
1 kể từ C ++ 11
+1
từ tôi.
Các lớp cục bộ đã được đề cập, nhưng đây là một cách để cho phép chúng xuất hiện nhiều hơn dưới dạng các hàm cục bộ, sử dụng một toán tử () quá tải và một lớp ẩn danh:
int main() {
struct {
unsigned int operator() (unsigned int val) const {
return val<=1 ? 1 : val*(*this)(val-1);
}
} fac;
std::cout << fac(5) << '\n';
}
Tôi không khuyên bạn nên sử dụng nó, đây chỉ là một mẹo vui (có thể làm được, nhưng tôi không nên).
Với sự phát triển của C ++ 11 một thời gian trước, giờ đây bạn có thể có các hàm cục bộ có cú pháp hơi gợi nhớ về JavaScript:
auto fac = [] (unsigned int val) {
return val*42;
};
operator () (unsigned int val)
, thiếu một bộ dấu ngoặc đơn.
std::sort()
, hoặc std::for_each()
.
auto
để khai báo biến. Stroustrup đưa ra ví dụ: function<void(char*b, char*e)> rev=[](char*b, char*e) { if( 1<e-b ) { swap( *b, *--e); rev(++b,e); } };
để đảo ngược một chuỗi con trỏ bắt đầu và kết thúc.
Không.
Bạn đang cố làm gì vậy?
cách giải quyết:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
Bắt đầu với C ++ 11, bạn có thể sử dụng lambdas thích hợp . Xem các câu trả lời khác để biết thêm chi tiết.
Câu trả lời cũ: Bạn có thể, sắp xếp, nhưng bạn phải gian lận và sử dụng một lớp giả:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
Như những người khác đã đề cập, bạn có thể sử dụng các hàm lồng nhau bằng cách sử dụng các phần mở rộng ngôn ngữ gnu trong gcc. Nếu bạn (hoặc dự án của bạn) dính vào chuỗi công cụ gcc, mã của bạn sẽ chủ yếu di động trên các kiến trúc khác nhau được nhắm mục tiêu bởi trình biên dịch gcc.
Tuy nhiên, nếu có một yêu cầu khả thi là bạn có thể cần phải biên dịch mã với một chuỗi công cụ khác, thì tôi sẽ tránh xa các tiện ích mở rộng đó.
Tôi cũng cẩn thận khi sử dụng các hàm lồng nhau. Chúng là một giải pháp tuyệt vời để quản lý cấu trúc của các khối mã phức tạp nhưng gắn kết (các phần không dành cho sử dụng bên ngoài / chung.) Chúng cũng rất hữu ích trong việc kiểm soát ô nhiễm không gian tên (một mối quan tâm rất thực với phức tạp tự nhiên / các lớp học dài trong các ngôn ngữ dài dòng.)
Nhưng giống như bất cứ điều gì, họ có thể được mở để lạm dụng.
Thật đáng buồn khi C / C ++ không hỗ trợ các tính năng như một tiêu chuẩn. Hầu hết các biến thể pascal và Ada đều làm (hầu hết tất cả các ngôn ngữ dựa trên Algol đều có). Tương tự với JavaScript. Tương tự với các ngôn ngữ hiện đại như Scala. Tương tự với các ngôn ngữ đáng kính như Erlang, Lisp hoặc Python.
Và cũng giống như với C / C ++, thật không may, Java (mà tôi kiếm được phần lớn cuộc sống của mình) thì không.
Tôi đề cập đến Java ở đây vì tôi thấy một số áp phích gợi ý sử dụng các lớp và phương thức của lớp như là các lựa chọn thay thế cho các hàm lồng nhau. Và đó cũng là cách giải quyết điển hình trong Java.
Câu trả lời ngắn gọn: Không.
Làm như vậy có xu hướng giới thiệu sự phức tạp giả tạo, không cần thiết trên một hệ thống phân cấp lớp. Với tất cả mọi thứ đều bằng nhau, lý tưởng là có một hệ thống phân cấp lớp (và các không gian tên và phạm vi bao gồm của nó) đại diện cho một miền thực tế càng đơn giản càng tốt.
Các hàm lồng nhau giúp đối phó với "riêng tư", độ phức tạp trong hàm. Thiếu các cơ sở đó, người ta nên cố gắng tránh truyền bá sự phức tạp "riêng tư" đó ra và vào mô hình lớp học của một người.
Trong phần mềm (và trong bất kỳ ngành kỹ thuật nào), mô hình hóa là vấn đề đánh đổi. Do đó, trong cuộc sống thực, sẽ có những ngoại lệ hợp lý cho những quy tắc đó (hay đúng hơn là hướng dẫn). Tiến hành cẩn thận, mặc dù.
Bạn không thể có các chức năng cục bộ trong C ++. Tuy nhiên, C ++ 11 có lambdas . Lambdas về cơ bản là các biến hoạt động như các hàm.
Một lambda có loại std::function
( thực tế điều đó không hoàn toàn đúng , nhưng trong hầu hết các trường hợp, bạn có thể cho rằng nó là). Để sử dụng loại này, bạn cần phải #include <functional>
. std::function
là một mẫu, lấy làm đối số mẫu kiểu trả về và kiểu đối số, với cú pháp std::function<ReturnType(ArgumentTypes)
. Ví dụ, std::function<int(std::string, float)>
là một lambda trả về một int
và lấy hai đối số, một std::string
và một float
. Một trong những phổ biến nhất là std::function<void()>
, trả về không có gì và không có đối số.
Khi lambda được khai báo, nó được gọi giống như một hàm bình thường, sử dụng cú pháp lambda(arguments)
.
Để xác định lambda, hãy sử dụng cú pháp [captures](arguments){code}
(có nhiều cách khác để làm điều đó, nhưng tôi sẽ không đề cập đến chúng ở đây). arguments
là những gì đối số lambda đưa ra, và code
là mã nên được chạy khi lambda được gọi. Thông thường bạn đặt [=]
hoặc [&]
như chụp. [=]
có nghĩa là bạn nắm bắt tất cả các biến trong phạm vi mà giá trị được xác định theo giá trị, có nghĩa là chúng sẽ giữ giá trị mà chúng có khi lambda được khai báo. [&]
có nghĩa là bạn nắm bắt tất cả các biến trong phạm vi bằng tham chiếu, có nghĩa là chúng sẽ luôn có giá trị hiện tại của chúng, nhưng nếu chúng bị xóa khỏi bộ nhớ thì chương trình sẽ bị sập. Dưới đây là một số ví dụ:
#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;
}
Bạn cũng có thể nắm bắt các biến cụ thể bằng cách chỉ định tên của chúng. Chỉ cần xác định tên của họ sẽ nắm bắt chúng theo giá trị, chỉ định tên của họ &
trước đó sẽ nắm bắt chúng bằng cách tham chiếu. Ví dụ: [=, &foo]
sẽ nắm bắt tất cả các biến theo giá trị ngoại trừ foo
sẽ được bắt bởi tham chiếu và [&, foo]
sẽ nắm bắt tất cả các biến bằng tham chiếu ngoại trừ foo
sẽ được bắt theo giá trị. Bạn cũng có thể chỉ chụp các biến cụ thể, ví dụ [&foo]
sẽ chụp foo
theo tham chiếu và sẽ không bắt được các biến khác. Bạn cũng có thể nắm bắt không có biến nào bằng cách sử dụng []
. Nếu bạn cố gắng sử dụng một biến trong lambda mà bạn không nắm bắt được, nó sẽ không biên dịch. Đây là một ví dụ:
#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;
}
Bạn không thể thay đổi giá trị của một biến được bắt bởi giá trị bên trong lambda (các biến được bắt bởi giá trị có một const
loại bên trong lambda). Để làm như vậy, bạn cần phải nắm bắt các biến bằng cách tham khảo. Đây là một 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;
}
Ngoài ra, gọi lambdas chưa được khởi tạo là hành vi không xác định và thường sẽ khiến chương trình bị sập. Ví dụ: không bao giờ làm điều này:
std::function<void()> lambda;
lambda(); //Undefined behavior because lambda is uninitialized
Ví dụ
Đây là mã cho những gì bạn muốn làm trong câu hỏi của bạn bằng 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;
}
Đây là một ví dụ nâng cao hơn về 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;
}
Không, nó không được phép. Cả C và C ++ đều không hỗ trợ tính năng này theo mặc định, tuy nhiên TonyK chỉ ra (trong các bình luận) rằng có các phần mở rộng cho trình biên dịch GNU C cho phép hành vi này trong C.
Tất cả các thủ thuật này chỉ xem (ít nhiều) là các hàm cục bộ, nhưng chúng không hoạt động như vậy. Trong một hàm cục bộ, bạn có thể sử dụng các biến cục bộ của các hàm siêu của nó. Đó là loại bán toàn cầu. Không phải những thủ thuật này có thể làm điều đó. Gần nhất là thủ thuật lambda từ c ++ 0x, nhưng đóng của nó bị ràng buộc trong thời gian định nghĩa, không phải thời gian sử dụng.
Bạn không thể định nghĩa một hàm miễn phí bên trong hàm khác trong C ++.
Hãy để tôi đăng một giải pháp ở đây cho C ++ 03 mà tôi cho là sạch nhất có thể. *
#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;
}
(*) trong thế giới C ++ sử dụng macro không bao giờ được coi là sạch.
Nhưng chúng ta có thể khai báo một hàm bên trong hàm main ():
int main()
{
void a();
}
Mặc dù cú pháp là chính xác, đôi khi nó có thể dẫn đến "phân tích cú pháp nhất":
#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());
}
=> không có đầu ra chương trình.
(Chỉ cảnh báo Clang sau khi biên dịch).