Maksud saya sesuatu seperti:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Maksud saya sesuatu seperti:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Jawaban:
Dalam versi c ++ (C ++ 11, C ++ 14, dan C ++ 17) saat ini, Anda dapat memiliki fungsi di dalam fungsi dalam bentuk 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 juga dapat memodifikasi variabel lokal melalui ** capture-by-reference *. Dengan menangkap-oleh-referensi, lambda memiliki akses ke semua variabel lokal yang dinyatakan dalam cakupan lambda. Itu dapat memodifikasi dan mengubahnya secara normal.
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 ++ tidak mendukung itu secara langsung.
Yang mengatakan, Anda dapat memiliki kelas-kelas lokal, dan mereka dapat memiliki fungsi (bukan static
atau static
), sehingga Anda dapat memperluasnya, meskipun itu sedikit membosankan:
int main() // it's int, dammit!
{
struct X { // struct's as good as class
static void a()
{
}
};
X::a();
return 0;
}
Namun, saya akan mempertanyakan praksis. Semua orang tahu (yah, sekarang Anda tahu, :)
C ++ tidak mendukung fungsi lokal, jadi mereka terbiasa tidak memilikinya. Namun, itu tidak digunakan untuk lumpur itu. Saya akan menghabiskan waktu cukup lama pada kode ini untuk memastikan itu benar-benar hanya ada untuk memungkinkan fungsi lokal. Tidak baik.
int main()
danint main(int argc, char* argv[])
int main()
dan int main(int argc, char* argv[])
harus didukung dan yang lain mungkin didukung tetapi mereka semua memiliki int kembali.
Untuk semua maksud dan tujuan, C ++ mendukung ini melalui lambdas : 1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
Di sini, f
adalah objek lambda yang bertindak sebagai fungsi lokal di main
. Capture dapat ditentukan untuk memungkinkan fungsi mengakses objek lokal.
Di belakang layar, f
adalah objek fungsi (yaitu objek dari jenis yang menyediakan sebuah operator()
). Tipe objek fungsi dibuat oleh kompiler berdasarkan pada lambda.
1 sejak C ++ 11
+1
dari saya.
Kelas-kelas lokal telah disebutkan, tetapi berikut ini adalah cara untuk menjadikannya lebih sebagai fungsi lokal, menggunakan operator () yang berlebihan dan kelas anonim:
int main() {
struct {
unsigned int operator() (unsigned int val) const {
return val<=1 ? 1 : val*(*this)(val-1);
}
} fac;
std::cout << fac(5) << '\n';
}
Saya tidak menyarankan untuk menggunakan ini, itu hanya trik lucu (bisa dilakukan, tetapi seharusnya tidak).
Dengan munculnya C ++ 11 beberapa waktu lalu, Anda sekarang dapat memiliki fungsi lokal yang sintaksnya sedikit mengingatkan pada JavaScript:
auto fac = [] (unsigned int val) {
return val*42;
};
operator () (unsigned int val)
, Anda kehilangan satu set kurung.
std::sort()
, atau std::for_each()
.
auto
untuk mendeklarasikan variabel. Stroustrup memberikan contoh: function<void(char*b, char*e)> rev=[](char*b, char*e) { if( 1<e-b ) { swap( *b, *--e); rev(++b,e); } };
untuk membalikkan string yang diberikan awal dan akhir pointer.
Tidak.
Apa yang sedang Anda coba lakukan?
solusi:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
Dimulai dengan C ++ 11 Anda dapat menggunakan lambdas yang tepat . Lihat jawaban lain untuk lebih jelasnya.
Jawaban lama: Anda bisa, semacam, tetapi Anda harus menipu dan menggunakan kelas dummy:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
Seperti yang disebutkan orang lain, Anda dapat menggunakan fungsi bersarang dengan menggunakan ekstensi bahasa gnu di gcc. Jika Anda (atau proyek Anda) menempel pada rantai alat gcc, kode Anda sebagian besar portabel di berbagai arsitektur yang ditargetkan oleh kompiler gcc.
Namun, jika ada persyaratan yang mungkin Anda perlukan untuk mengkompilasi kode dengan toolchain yang berbeda, maka saya akan tinggal jauh dari ekstensi tersebut.
Saya juga melangkah dengan hati-hati saat menggunakan fungsi bersarang. Mereka adalah solusi yang indah untuk mengelola struktur blok kode yang rumit, namun kohesif (potongan-potongan yang tidak dimaksudkan untuk penggunaan eksternal / umum). Mereka juga sangat membantu dalam mengendalikan polusi namespace (perhatian yang sangat nyata dengan kompleks alami / kelas panjang dalam bahasa verbose.)
Tapi seperti apa pun, mereka bisa terbuka terhadap pelecehan.
Sangat menyedihkan bahwa C / C ++ tidak mendukung fitur-fitur seperti standar. Kebanyakan varian pascal dan Ada lakukan (hampir semua bahasa berbasis Algol melakukannya). Sama dengan JavaScript. Sama dengan bahasa modern seperti Scala. Sama dengan bahasa terhormat seperti Erlang, Lisp atau Python.
Dan seperti halnya dengan C / C ++, sayangnya, Java (dengan mana saya mendapatkan sebagian besar hidup saya) tidak.
Saya menyebutkan Java di sini karena saya melihat beberapa poster menyarankan penggunaan kelas dan metode kelas sebagai alternatif untuk fungsi bersarang. Dan itu juga solusi khas di Jawa.
Jawaban singkat: Tidak.
Melakukannya cenderung memperkenalkan kompleksitas buatan, yang tidak perlu pada hierarki kelas. Dengan semua hal yang sama, yang ideal adalah memiliki hierarki kelas (dan ruang lingkup dan cakupannya) yang mewakili domain aktual sesederhana mungkin.
Fungsi bersarang membantu menangani kompleksitas "pribadi" dalam fungsi. Karena kekurangan fasilitas-fasilitas itu, orang harus berusaha menghindari penyebaran kompleksitas "pribadi" itu ke dalam model kelasnya.
Dalam perangkat lunak (dan dalam disiplin ilmu teknik apa pun), pemodelan merupakan masalah pertukaran. Dengan demikian, dalam kehidupan nyata, akan ada pengecualian yang dibenarkan untuk aturan-aturan tersebut (atau lebih tepatnya pedoman). Lanjutkan dengan hati-hati.
Anda tidak dapat memiliki fungsi lokal di C ++. Namun, C ++ 11 memiliki lambdas . Lambdas pada dasarnya adalah variabel yang berfungsi seperti fungsi.
Lambda memiliki tipe std::function
( sebenarnya itu tidak sepenuhnya benar , tetapi dalam kebanyakan kasus Anda dapat mengira itu adalah). Untuk menggunakan jenis ini, Anda harus #include <functional>
. std::function
adalah templat, dengan argumen templat, tipe pengembalian dan tipe argumen, dengan sintaks std::function<ReturnType(ArgumentTypes)
. Misalnya, std::function<int(std::string, float)>
adalah lambda mengembalikan int
dan mengambil dua argumen, satu std::string
dan satu float
. Yang paling umum adalahstd::function<void()>
, yang tidak menghasilkan apa-apa dan tidak mengambil argumen.
Setelah lambda dideklarasikan, ia dipanggil seperti fungsi normal, menggunakan sintaks lambda(arguments)
.
Untuk mendefinisikan lambda, gunakan sintaks [captures](arguments){code}
(ada cara lain untuk melakukannya, tapi saya tidak akan menyebutkannya di sini). arguments
adalah argumen yang diambil oleh lambda, dan code
merupakan kode yang harus dijalankan ketika lambda dipanggil. Biasanya Anda menempatkan [=]
atau [&]
sebagai tangkapan. [=]
berarti bahwa Anda menangkap semua variabel dalam lingkup di mana nilai ditentukan oleh nilai, yang berarti bahwa mereka akan menyimpan nilai yang mereka miliki ketika lambda dideklarasikan. [&]
berarti bahwa Anda menangkap semua variabel dalam lingkup dengan referensi, yang berarti bahwa mereka akan selalu memiliki nilai saat ini, tetapi jika mereka dihapus dari memori, program akan macet. Berikut ini beberapa contohnya:
#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;
}
Anda juga dapat menangkap variabel tertentu dengan menentukan nama mereka. Hanya menentukan nama mereka akan menangkap mereka dengan nilai, menentukan nama mereka dengan &
sebelum akan menangkap mereka dengan referensi. Misalnya, [=, &foo]
akan menangkap semua variabel dengan nilai kecuali foo
yang akan ditangkap oleh referensi, dan [&, foo]
akan menangkap semua variabel dengan referensi kecuali foo
yang akan ditangkap oleh nilai. Anda juga dapat menangkap hanya variabel tertentu, misalnya [&foo]
akan menangkap foo
dengan referensi dan tidak akan menangkap variabel lain. Anda juga dapat menangkap tidak ada variabel sama sekali dengan menggunakan []
. Jika Anda mencoba menggunakan variabel dalam lambda yang tidak Anda tangkap, itu tidak akan dikompilasi. Berikut ini sebuah contoh:
#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;
}
Anda tidak dapat mengubah nilai variabel yang ditangkap oleh nilai di dalam lambda (variabel yang ditangkap oleh nilai memiliki const
jenis di dalam lambda). Untuk melakukannya, Anda perlu menangkap variabel dengan referensi. Inilah contohnya:
#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;
}
Juga, memanggil lambdas tidak diinisialisasi adalah perilaku yang tidak terdefinisi dan biasanya akan menyebabkan program macet. Misalnya, jangan pernah melakukan ini:
std::function<void()> lambda;
lambda(); //Undefined behavior because lambda is uninitialized
Contohnya
Berikut adalah kode untuk apa yang ingin Anda lakukan dalam pertanyaan Anda menggunakan 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;
}
Berikut adalah contoh lambda yang lebih maju:
#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;
}
Tidak, itu tidak diizinkan. Baik C maupun C ++ tidak mendukung fitur ini secara default, namun TonyK menunjukkan (dalam komentar) bahwa ada ekstensi ke kompiler GNU C yang memungkinkan perilaku ini dalam C.
Semua trik ini hanya terlihat (kurang lebih) sebagai fungsi lokal, tetapi mereka tidak berfungsi seperti itu. Dalam fungsi lokal Anda dapat menggunakan variabel lokal dari fungsi super itu. Ini semacam semi-global. Tak satu pun dari trik ini bisa melakukannya. Yang paling dekat adalah trik lambda dari c ++ 0x, tetapi penutupannya terikat pada waktu definisi, bukan waktu penggunaan.
Anda tidak dapat mendefinisikan fungsi bebas di dalam yang lain di C ++.
Biarkan saya memposting solusi di sini untuk C ++ 03 yang saya anggap sebagai yang terbersih mungkin. *
#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;
}
(*) di dunia C ++ menggunakan makro tidak pernah dianggap bersih.
Tapi kita bisa mendeklarasikan fungsi di dalam main ():
int main()
{
void a();
}
Meskipun sintaksnya benar, kadang-kadang dapat menyebabkan "Parsing paling menjengkelkan":
#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());
}
=> tidak ada output program.
(Hanya peringatan dentang setelah kompilasi).