Gibi bir şey demek istiyorum:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Gibi bir şey demek istiyorum:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Yanıtlar:
Geçerli c ++ sürümlerinde (C ++ 11, C ++ 14 ve C ++ 17), işlevlerin içinde lambda biçiminde işlevler olabilir:
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 ayrıca yerel referansları ** referansla yakalama * ile değiştirebilir. Referansla yakalama ile lambda, lambda kapsamında açıklanan tüm yerel değişkenlere erişebilir. Bunları normal olarak değiştirebilir ve değiştirebilir.
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 ++ bunu doğrudan desteklemez.
Bununla birlikte, yerel sınıflara sahip olabilirsiniz ve işlevlere sahip olabilirler ( static
ya da değil static
), bu yüzden biraz çamur olsa da, bunu biraz uzatabilirsiniz:
int main() // it's int, dammit!
{
struct X { // struct's as good as class
static void a()
{
}
};
X::a();
return 0;
}
Ancak praksiyi sorgularım. Herkes biliyor (iyi, şimdi zaten, zaten :)
) C ++ yerel işlevleri desteklemiyor, bu yüzden bunlara sahip değiller. Ancak bunlar bu çamura alışık değildir. Gerçekten sadece yerel işlevlere izin vermek için bu kod üzerinde biraz zaman harcamak istiyorum. İyi değil.
int main()
veint main(int argc, char* argv[])
int main()
ve int main(int argc, char* argv[])
desteklenmeli ve diğerleri desteklenebilir ancak hepsinin dönüş int var.
Tüm niyet ve amaçlar için, C ++ lambdas aracılığıyla bunu destekler : 1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
Burada, f
yerel bir işlev olarak hareket eden bir lambda nesnesidir main
. Yakalama, işlevin yerel nesnelere erişmesine izin vermek için belirtilebilir.
Sahne arkasında f
bir işlev nesnesi (yani an sağlayan türden bir nesne operator()
). Fonksiyon nesnesi türü lambda tabanlı derleyici tarafından oluşturulur.
1 C ++ 11'den beri
+1
benden.
Yerel sınıflardan daha önce bahsedilmiş, ancak işleci () aşırı yükü ve anonim bir sınıf kullanarak yerel işlevler olarak daha fazla görünmelerine izin vermenin bir yolu:
int main() {
struct {
unsigned int operator() (unsigned int val) const {
return val<=1 ? 1 : val*(*this)(val-1);
}
} fac;
std::cout << fac(5) << '\n';
}
Bunu kullanmanızı tavsiye etmiyorum, sadece komik bir numara (yapabilir, ama imho olmamalı).
C ++ 11'in yükselişiyle birlikte, söz dizimi JavaScript'i biraz hatırlatan yerel işlevlere sahip olabilirsiniz:
auto fac = [] (unsigned int val) {
return val*42;
};
operator () (unsigned int val)
, eksik bir parantez kümesi.
std::sort()
ya std::for_each()
.
auto
değişkeni bildirmek için kullanamazsınız . Stroustrup, örneğin başlangıç function<void(char*b, char*e)> rev=[](char*b, char*e) { if( 1<e-b ) { swap( *b, *--e); rev(++b,e); } };
ve bitiş işaretçileri verilen bir dizgeyi tersine çevirmek için örnek verir .
Hayır.
Ne yapmaya çalışıyorsun?
geçici çözüm:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
C ++ 11 ile başlayarak uygun lambdaları kullanabilirsiniz . Daha fazla ayrıntı için diğer cevaplara bakın.
Eski cevap: Yapabilirsin, ama kukla bir sınıf almalısın ve kullanmalısın:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
Diğerlerinin de belirttiği gibi, gcc'de gnu dil uzantılarını kullanarak iç içe geçmiş işlevleri kullanabilirsiniz. Siz (veya projeniz) gcc araç zincirine yapışırsanız, kodunuz çoğunlukla gcc derleyicisinin hedeflediği farklı mimariler arasında taşınabilir olacaktır.
Ancak, kodu farklı bir araç zinciriyle derlemeniz gerekebilecek olası bir gereksinim varsa, bu tür uzantılardan uzak dururum.
Ayrıca, iç içe geçmiş işlevleri kullanırken de dikkatli davranırım. Karmaşık, ancak birbirine bağlı kod bloklarının (parçaları harici / genel kullanım için amaçlanmayan) yapısını yönetmek için güzel bir çözümdür. Ayrıca ad alanı kirliliğini kontrol etmede çok yararlıdırlar (doğal olarak karmaşık / ayrıntılı dillerde uzun sınıflar.)
Ama her şey gibi, onlar da istismara açık olabilirler.
C / C ++ 'nın standart gibi özellikleri desteklememesi üzücü. Çoğu paskal varyant ve Ada yapar (neredeyse tüm Algol tabanlı diller yapar). JavaScript ile aynı. Scala gibi modern dillerle aynı. Erlang, Lisp veya Python gibi saygıdeğer dillerle aynı.
Ve C / C ++ ile olduğu gibi maalesef Java (benim hayatımın çoğunu kazandığım) bunu yapmaz.
Burada Java'dan bahsediyorum çünkü sınıfların ve sınıf yöntemlerinin iç içe işlevlere alternatif olarak kullanılmasını öneren birkaç poster görüyorum. Bu da Java'daki tipik çözümdür.
Kısa cevap: Hayır.
Bunu yapmak, sınıf hiyerarşisine yapay, gereksiz karmaşıklık getirme eğilimindedir. Her şey eşit olduğunda, ideal, gerçek bir alanı mümkün olduğunca basit bir şekilde temsil eden bir sınıf hiyerarşisine (ve onu kapsayan ad alanlarına ve kapsamlarına) sahip olmaktır.
Yuvalanmış işlevler, işlev içinde karmaşıklığın "özel" olarak ele alınmasına yardımcı olur. Bu tesislerden yoksun olan kişi, bu "özel" karmaşıklığı sınıf modeline yaymaktan kaçınmaya çalışmalıdır.
Yazılımda (ve herhangi bir mühendislik disiplininde), modelleme bir değiş tokuş meselesidir. Böylece, gerçek hayatta, bu kuralların (veya daha doğrusu kılavuzların) haklı istisnaları olacaktır. Yine de dikkatli olun.
C ++ 'da yerel işlevleriniz olamaz. Bununla birlikte, C ++ 11'in lambdaları vardır . Lambdalar temelde fonksiyonlar gibi çalışan değişkenlerdir.
Bir lambda türü vardır std::function
( aslında bu oldukça doğru değildir , ancak çoğu durumda öyle olduğunu varsayabilirsiniz). Bu türü kullanmak için kullanmanız gerekir #include <functional>
. std::function
sözdizimi ile dönüş argümanı ve argüman türlerini şablon argümanı olarak alan bir şablondur std::function<ReturnType(ArgumentTypes)
. Örneğin std::function<int(std::string, float)>
, bir döndüren int
ve bir std::string
ve bir olmak üzere iki argüman alan bir lambdadır float
. En yaygın olanı, std::function<void()>
hiçbir şey döndürmez ve argüman almaz.
Bir lambda bildirildikten sonra, sözdizimi kullanılarak normal bir işlev gibi çağrılır lambda(arguments)
.
Bir lambda tanımlamak için sözdizimini kullanın [captures](arguments){code}
(bunu yapmanın başka yolları vardır, ancak burada bahsetmeyeceğim). arguments
lambda'nın aldığı argümanlar ve lambda code
çağrıldığında çalıştırılması gereken koddur. Genellikle koyar [=]
ya [&]
da yakalarsınız. [=]
bu, değerin değerle tanımlandığı kapsamdaki tüm değişkenleri yakaladığınız anlamına gelir. Bu, lambda bildirildiğinde sahip oldukları değeri koruyacakları anlamına gelir. [&]
kapsamdaki tüm değişkenleri referans olarak yakaladığınız anlamına gelir, yani her zaman geçerli değerlerine sahip olurlar, ancak bellekten silinirlerse program çökecektir. İşte bazı örnekler:
#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;
}
Belirli değişkenleri adlarını belirterek de yakalayabilirsiniz. Sadece isimlerini belirtmek onları değere göre yakalar, isimlerini daha &
önce belirtmek onları referans olarak yakalar. Örneğin, referans tarafından yakalanacak [=, &foo]
olanlar hariç tüm değişkenleri değere göre yakalar ve değer tarafından foo
yakalanacak [&, foo]
olanlar dışında tüm değişkenleri referans olarak yakalar foo
. Örnek için de, sadece belirli değişkenler yakalayabilir [&foo]
yakalayacaktır foo
referans olarak ve başka hiçbir değişkenleri ele geçirecek. Düğmesini kullanarak hiçbir değişkeni yakalayamazsınız []
. Eğer yakalamadığınız bir lambda'da bir değişken kullanmaya çalışırsanız, derlenmez. İşte bir örnek:
#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 içindeki değerle yakalanan bir değişkenin değerini değiştiremezsiniz (değerle yakalanan değişkenlerin const
lambda içinde bir türü vardır). Bunu yapmak için değişkeni başvuru ile yakalamanız gerekir. İşte bir örnek:
#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;
}
Ayrıca, başlatılmamış lambdas çağırmak tanımsız bir davranıştır ve genellikle programın çökmesine neden olur. Örneğin, bunu asla yapmayın:
std::function<void()> lambda;
lambda(); //Undefined behavior because lambda is uninitialized
Örnekler
Lamdas kullanarak sorunuzda ne yapmak istediğinize ilişkin kod:
#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;
}
İşte lambda'nın daha gelişmiş bir örneği:
#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;
}
Hayır, izin verilmiyor. Ne C ne de C ++ bu özelliği varsayılan olarak desteklemez, ancak TonyK (yorumlarda), C'de bu davranışı etkinleştiren GNU C derleyicisinin uzantıları olduğuna dikkat çeker.
Tüm bu püf noktaları sadece (az ya da çok) yerel işlevler olarak görünüyor, ancak böyle çalışmıyorlar. Yerel bir işlevde, süper işlevlerinin yerel değişkenlerini kullanabilirsiniz. Bir çeşit yarı küresel. Bu hilelerin hiçbiri bunu yapamaz. En yakın olan c ++ 0x'den lambda hüneridir, ancak kapanış kullanım süresine değil, tanım süresine bağlıdır.
C ++ 'da başka bir fonksiyonun içinde serbest fonksiyon tanımlayamazsınız.
Burada mümkün olan en temiz olduğunu düşündüğüm C ++ 03 için bir çözüm göndereyim. *
#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 ++ dünyasında makro kullanan hiçbir zaman temiz kabul edilmez.
Ancak main () içindeki bir işlevi bildirebiliriz:
int main()
{
void a();
}
Sözdizimi doğru olmasına rağmen, bazen "En sinir bozucu ayrıştırmaya" yol açabilir:
#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());
}
=> program çıkışı yok.
(Sadece derlemeden sonra Clang uyarısı).