ฉันหมายถึงสิ่งที่ชอบ:
int main()
{
void a()
{
// code
}
a();
return 0;
}
ฉันหมายถึงสิ่งที่ชอบ:
int main()
{
void a()
{
// code
}
a();
return 0;
}
คำตอบ:
ในรุ่นปัจจุบันของ 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!");
}
}
แลมบ์ดายังสามารถปรับเปลี่ยนตัวแปรในท้องถิ่นผ่านทาง ** การจับภาพโดยการอ้างอิง * ด้วยการอ้างอิงโดยการจับแลมบ์ดามีการเข้าถึงตัวแปรท้องถิ่นทั้งหมดที่ประกาศในขอบเขตของแลมบ์ดา สามารถแก้ไขและเปลี่ยนแปลงได้ตามปกติ
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
) เพื่อให้คุณสามารถขยายนี้แม้ว่าจะเป็น kludge:
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[])
int main()
และint main(int argc, char* argv[])
ต้องได้รับการสนับสนุนและอื่น ๆ อาจได้รับการสนับสนุน
สำหรับทุกเจตนาและวัตถุประสงค์ C ++ สนับสนุนสิ่งนี้ผ่านlambdas : 1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
นี่f
คือแลมบ์ดาวัตถุที่ทำหน้าที่เป็นฟังก์ชันท้องถิ่นmain
มา สามารถระบุการจับเพื่ออนุญาตให้ฟังก์ชันเข้าถึงวัตถุในท้องถิ่นได้
ด้านหลังของฉากf
เป็นวัตถุฟังก์ชั่น (เช่นวัตถุประเภทที่ให้operator()
) ประเภทฟังก์ชั่นวัตถุที่ถูกสร้างขึ้นโดยคอมไพเลอร์ขึ้นอยู่กับแลมบ์ดา
1ตั้งแต่ C ++ 11
+1
จากฉัน
คลาสของโลคัลได้ถูกกล่าวถึงแล้ว แต่นี่เป็นวิธีที่จะทำให้พวกมันปรากฏมากขึ้นในฐานะที่เป็นฟังก์ชั่นท้องถิ่นโดยใช้โอเปอเรเตอร์ () โอเวอร์โหลดและคลาสที่ไม่ระบุชื่อ:
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;
};
operator () (unsigned int val)
เป็นชุดวงเล็บที่ขาดหายไปของคุณ
std::sort()
std::for_each()
auto
ประกาศตัวแปรได้ Stroustrup ให้ตัวอย่าง: function<void(char*b, char*e)> rev=[](char*b, char*e) { if( 1<e-b ) { swap( *b, *--e); rev(++b,e); } };
สำหรับการย้อนกลับสตริงที่กำหนดพอยน์เตอร์เริ่มต้นและสิ้นสุด
เลขที่
คุณพยายามจะทำอะไร?
วิธีแก้ปัญหา:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
เริ่มต้นด้วย C ++ 11 คุณสามารถใช้ที่เหมาะสมlambdas ดูคำตอบอื่น ๆ สำหรับรายละเอียดเพิ่มเติม
คำตอบเก่า: คุณสามารถเรียงลำดับของ แต่คุณต้องโกงและใช้ชั้นเรียนหุ่น:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
ดังที่คนอื่น ๆ พูดถึงคุณสามารถใช้ฟังก์ชั่นที่ซ้อนกันได้โดยใช้นามสกุลภาษา gnu เป็น gcc หากคุณ (หรือโครงการของคุณ) ยึดติดกับ gcc toolchain โค้ดของคุณจะเป็นแบบพกพาส่วนใหญ่ในสถาปัตยกรรมที่แตกต่างกันที่กำหนดเป้าหมายโดยคอมไพเลอร์ gcc
อย่างไรก็ตามหากมีความต้องการที่เป็นไปได้ที่คุณอาจต้องรวบรวมรหัสด้วย toolchain ที่แตกต่างกันแล้วฉันจะอยู่ห่างจากส่วนขยายดังกล่าว
ฉันจะใช้อย่างระมัดระวังด้วยการใช้ฟังก์ชันซ้อนกัน พวกเขาเป็นทางออกที่สวยงามสำหรับการจัดการโครงสร้างของความซับซ้อน แต่บล็อกของรหัสที่เหนียวแน่น (ชิ้นส่วนที่ไม่ได้มีไว้สำหรับการใช้งานภายนอก / ทั่วไป) พวกเขายังมีประโยชน์มากในการควบคุมมลภาวะเนมสเปซ คลาสยาวในภาษา verbose)
แต่พวกเขาสามารถเปิดรับการละเมิดได้
เป็นเรื่องน่าเศร้าที่ C / C ++ ไม่รองรับคุณสมบัติเช่นมาตรฐาน ปาสกาลสายพันธุ์ส่วนใหญ่และ Ada ทำ (เกือบทุกภาษาที่ใช้ Algol ทำ) เหมือนกันกับ JavaScript เช่นเดียวกันกับภาษาสมัยใหม่เช่น Scala เช่นเดียวกันกับภาษาที่น่านับถือเช่น Erlang, Lisp หรือ Python
และเช่นเดียวกับ C / C ++ แต่น่าเสียดายที่ Java (ซึ่งฉันได้รับส่วนใหญ่ของชีวิตของฉัน) ไม่ได้
ฉันพูดถึง Java ที่นี่เพราะฉันเห็นหลายโปสเตอร์แนะนำให้ใช้วิธีการเรียนและชั้นเรียนเป็นทางเลือกแทนฟังก์ชั่นที่ซ้อนกัน และนั่นก็เป็นวิธีแก้ปัญหาทั่วไปใน Java
คำตอบสั้น ๆ : ไม่
การทำเช่นนี้มีแนวโน้มที่จะแนะนำความซับซ้อนเทียมโดยไม่จำเป็นในลำดับชั้นของชั้นเรียน ด้วยทุกสิ่งที่เท่าเทียมกันอุดมคติคือการมีลำดับชั้นของคลาส (และเนมสเปซที่ครอบคลุมและขอบเขต) ที่แสดงถึงโดเมนจริงที่เรียบง่ายที่สุดเท่าที่จะทำได้
ฟังก์ชั่นที่ซ้อนกันช่วยจัดการกับ "ความเป็นส่วนตัว" ความซับซ้อนภายในฟังก์ชั่น ขาดสิ่งอำนวยความสะดวกเหล่านั้นเราควรพยายามหลีกเลี่ยงการเผยแพร่ความซับซ้อนของ "ส่วนตัว" ออกไปสู่รูปแบบการเรียน
ในซอฟต์แวร์ (และในสาขาวิศวกรรมใด ๆ ) การสร้างแบบจำลองเป็นเรื่องของการแลกเปลี่ยน ดังนั้นในชีวิตจริงจะมีการยกเว้นข้อยกเว้นสำหรับกฎเหล่านั้น (หรือแนวทาง) ดำเนินการต่อด้วยความระมัดระวัง
คุณไม่สามารถมีฟังก์ชั่นท้องถิ่นใน 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;
}
นอกจากนี้คุณยังสามารถจับตัวแปรเฉพาะโดยระบุชื่อของพวกเขา เพียงแค่ระบุชื่อของพวกเขาจะจับพวกเขาด้วยค่าการระบุชื่อของพวกเขาด้วย&
ก่อนจะจับพวกเขาโดยการอ้างอิง ตัวอย่างเช่น[=, &foo]
จะจับตัวแปรทั้งหมดตามค่ายกเว้นfoo
ซึ่งจะถูกบันทึกโดยการอ้างอิงและ[&, foo]
จะจับตัวแปรทั้งหมดโดยการอ้างอิงยกเว้นfoo
ซึ่งจะถูกดักจับด้วยค่า คุณสามารถจับเฉพาะตัวแปรที่เฉพาะเจาะจงเช่น[&foo]
จะจับภาพfoo
โดยการอ้างอิงและจะไม่จับตัวแปรอื่น ๆ []
นอกจากนี้คุณยังสามารถจับภาพตัวแปรที่ไม่ทั้งหมดโดยใช้ หากคุณพยายามใช้ตัวแปรในแลมบ์ดาที่คุณไม่ได้จับมันจะไม่รวบรวม นี่คือตัวอย่าง:
#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;
}
คุณไม่สามารถเปลี่ยนค่าของตัวแปรที่ถูกจับโดยค่าภายในแลมบ์ดา (ตัวแปรที่จับโดยค่าจะมี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;
}
นอกจากนี้การเรียก lambdas ที่ไม่ได้เตรียมการนั้นเป็นพฤติกรรมที่ไม่ได้กำหนดและมักจะทำให้โปรแกรมหยุดทำงาน ตัวอย่างเช่นอย่าทำสิ่งนี้:
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;
}
นี่คือตัวอย่างขั้นสูงของแลมบ์ดา:
#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;
}
ไม่ไม่อนุญาต C หรือ C ++ ไม่รองรับคุณสมบัตินี้ตามค่าเริ่มต้นอย่างไรก็ตาม TonyK ชี้ให้เห็น (ในความคิดเห็น) ว่ามีส่วนขยายของคอมไพเลอร์ GNU C ที่เปิดใช้งานลักษณะการทำงานนี้ใน C
เทคนิคทั้งหมดนี้แค่มอง (มากหรือน้อย) เป็นฟังก์ชั่นในท้องถิ่น แต่มันไม่ทำงานอย่างนั้น ในฟังก์ชั่นท้องถิ่นคุณสามารถใช้ตัวแปรท้องถิ่นของมันเป็นฟังก์ชั่นสุดยอด มันเป็นครึ่งวงกลม ไม่ใช่เทคนิคเหล่านี้สามารถทำได้ ที่ใกล้ที่สุดคือเคล็ดลับแลมบ์ดาจาก c ++ 0x แต่การปิดนั้นถูกผูกไว้ในเวลาที่กำหนดไม่ใช่เวลาที่ใช้
คุณไม่สามารถกำหนดฟังก์ชั่นฟรีได้ใน C ++
ให้ฉันโพสต์วิธีแก้ปัญหาที่นี่สำหรับ 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 ++ ที่ใช้มาโครนั้นไม่ถือเป็นความสะอาด
แต่เราสามารถประกาศฟังก์ชั่นภายใน 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());
}
=> ไม่มีเอาต์พุตโปรแกรม
(เฉพาะเสียงเตือนดังหลังจากรวบรวม)