2009-06-23 20 views
19

Benim gibi çoğu C++ programcıları bir noktada aşağıdaki hatayı yaptılar:Yerel işlev bildirimleri için herhangi bir kullanım var mı?

class C { /*...*/ }; 

int main() { 
    C c();  // declares a function c taking no arguments returning a C, 
      // not, as intended by most, an object c of type C initialized 
      // using the default constructor. 
    c.foo(); // compiler complains here. 

    //... 
} 

Artık hata oldukça açıkken bunu bildiğinizde, bu tür yerel işlev bildirimleri için herhangi bir mantıklı kullanım olup olmadığını merak ettim Bunu yapabileceğiniz - özellikle 'in gibi bir yerel fonksiyonun aynı blokta tanımlanmasının bir yolu olmadığı için; başka bir yerde tanımlamanız gerekiyor.

Bence, Java tarzı yerel sınıflar, özellikle anonim bir şekilde, sık kullanma eğiliminde olan oldukça hoş bir özellik. Yerel C++ sınıfları (hatta satır içi tanımlanmış üye işlevleri olabilir) bile bazı kullanımları vardır. Ama tanımsız bu yerel işlev beyanı bana çok garip geliyor. Sadece bir C-mirası mı yoksa farkında olmadığım bazı daha derin kullanım durumları mı var?

İnançsızlar için dosyasını düzenleyin: C c(), değil bir fonksiyon işaretçisi beyanıdır.

Bu program

int main() 
{ 
    void g(); 
    cout << "Hello "; 
    g(); 
    return 0; 
} 

void g() 
{ 
    cout << "world." << endl; 
} 

çıkış Hello world. Bu program

void fun() 
{ 
    cout << "world." << endl; 
} 

int main() 
{ 
    void g(); 
    g = fun; 
    cout << "Hello "; 
    g(); 
    return 0; 
} 

derlenmiyor. gcc şikayet ediyor:

error: cannot convert 'void()()' to 'void()()' in assignment

comeau:

error: expression must be a modifiable lvalue
+0

GCC hata mesajının yanlış olduğunu unutmayın (bilinen hata). "Error: 'void()' öğesini atamada 'void()' yapamaz", ancak aslında "type-id" işlevini yanlış olarak çözer. ("void()()" yerine, bir işlevi döndüren (yasadışı tip) bir işlev yerine, "void()") –

+0

ilgili soru: http://stackoverflow.com/questions/928992/nested -Functions-are-not-değil-ama-neden-nested-function-prototip-izin verilir –

+0

Gcc hata mesajı bir hata olduğundan emin değilim - aslında ekstra bir parantez çifti ile işlevi ilan edebilirsiniz void (g)() ', fonksiyon işaretçileri ile çalışırken gerekenler:' void * g() 'bir void *' döndüren bir fonksiyondur, void (* g)() 'bir fonksiyonun işaretçisidir. tür, iyi, 'void()()', yani hiçbir argüman almayan ve hiçbir şey döndürmeyen. – Tobias

cevap

2

Başka bir işleve argüman olarak aktarmak istediğimde C yerel işlev bildirimlerini kullanmak istedim. Bunu her zaman diğer dillerde yapıyorum. Bunun nedeni veri yapılarının uygulanmasını kapsüllemek.

E.g. Bazı veri yapılarını tanımlarım, ör. bir ağaç veya bir grafik, ve iç uygulamanın ayrıntılarını, örneğin: çünkü değiştirmek veya değiştirmek isteyebilirim. Bu yüzden elementleri için erişimci ve mutator fonksiyonlarını ve elementler üzerinde yinelemeyi sağlayan bir geçiş fonksiyonunu ortaya koyarım. Geçiş işlevi argümanı bir eleman üzerinde çalışan bir fonksiyona sahiptir; traversal fonksiyonunun görevi, argüman fonksiyonunu her eleman üzerinde yürütmek ve sonuçları bir şekilde toplamaktır. Şimdi geçiş işlevini çağırdığımda, argüman işlevi genellikle yerel duruma bağlı olan ve bu nedenle yerel bir işlev olarak tanımlanması gereken bazı özel işlemlerdir. Fonksiyonu, yerel durumu içeren değişkenlerle, küresel olmak için, ya da onları tutmak için özel olarak yaratılmış bir iç sınıfa itmek zorunda kalmak çirkin bir şeydir. Ama bu C++ ile değil C ve Java ile olan bir problem.

+0

Tamam, yani C++ 'da amacını kaybeden bir C mirası mı diyorsun? – Tobias

+0

Tam olarak değil. Aslında bu duruma nasıl hitap edeceğimi anlayabilmek için C++ 'da - şablonlarla ya da şablonsuz - yeterince deneyimli değilim. Yineleyiciler C++ 'da yaygındır, ancak argüman olarak işlev alan traversal yöntemler hakkında çok emin değilim. – reinierpost

+0

Güncelleştirme: bkz http://stackoverflow.com/questions/2116128/easier-way-to-do-callbacks-for-vectors-or-maybe-something-else-in-the-stl-c – reinierpost

2

bir ileri beyan prototip. Çok fazla yerel işleviniz varsa veya tanım sırasına aykırı olan yerel işlevleri varsa, buna ihtiyacınız olabilir. Bu çok daha iyi çözüm bulunmaktadır Tabii

int main() 
{ 
    void doSomething(); 
    doSomething(); 
    return 0; 
} 

void otherFunc() 
{ 
    doSomething(); // ERROR, doSomething() not in scope 
} 

void doSomething() 
{ 
    ... 
} 

: Aklıma

+0

Burada sizin noktanızı anlamıyorum: Usecase'inde, kaynak dosyanın başlangıcında (hatta bazı özel başlıklarda) tüm işlevlerini bildiririm ve daha sonra mutlu bir şekilde uygular. Neden başka bir işlevin içinde bir funciton/_declare_ istedim? – Tobias

10

tek kullanım işlevi bildirimleri kapsamını azaltmaktır. Bir işlevi gizlemeniz gerekiyorsa, kodunuzu gerçekten ayrı ayrı modüllere taşıyarak kodunuzu yeniden yapılandırmanız gerekir, böylece gizlemek istediğiniz işlevi çağırması gereken işlevlerin hepsi aynı modülde olur. Daha sonra, bu işlev modülünü, statik (C yolu) olarak veya anonim bir ad boşluğuna (C++ yolu) yerleştirerek bildirebilirsiniz.

2

Görebildiğim tek aklı kullanımı, bir derleme ünitesinde sadece bir fonksiyonun başka bir derleme ünitesinde tanımlanmış bir fonksiyonu tanımasını sağlamaktır. Sanırım bu biraz makul bir kullanım olurdu, ama aklıma gelen tek kişi bu ve bence bu aşırı derecede.

+0

Ama neden başka bir tanıma ait bir fonksiyon tanımladım (fonksiyon tanımının dışında veya bir başlıkta olduğu gibi)? – Tobias

+1

Dediğim gibi, sadece bu işlev görebiliyordu. –

-1

Parametre almayan bir işlevi bildirme ve varsayılan bir yok edici ile bir sınıf oluşturma arasında farklılık göstermek isterseniz, parantezi atlayın.

class C 
{ 
    public: 
    void foo() {} 
}; 


int main() 
{ 
    // declare function d, taking no arguments, returning fresh C 
    C d(); 

    // instantiate class (leave parenthesis out) 
    C c; 
    c.foo(); 

    // call declared function 
    C goo = d(); 

    return 0; 
} 

C d() 
{ 
    C c; 
    // .. 
    return c; 
} 
+1

Benim sorum bir nesneyi nasıl örneklendireceğimiz değil, sorum şu, neden d işlevini sizin yaptığınız şekilde ilan edersiniz (ana dışında bildirmek yerine)? – Tobias

0

Bazen biz doğru ilk kullanımdan önce değişkenleri bildirmek için teşvik edilmektedir aynı nedenle bunu (ve öncesi), yani okunabilirliği artırmak için. (Evet, değişkenler için daha önemli olduğunu anlıyorum çünkü değişkenin ilk kullanımının ne olduğunu düşünmeden önce kullanıp kullanmadığını kontrol etme ihtiyacını gidermektedir).Prototipin (özellikle yalnızca c()'dan daha fazla ilgiliyse) işlev çağırma işlevine sahip olması okunabilirliği artırır. C c() numaralı özel durumun insanlara yanıltıcı olması talihsiz bir durumdur, ancak genel olarak işlevlerin yerel olarak ilan edilmesi fikri haklıdır. Elbette, bu zaten kapsamda olan işlev adları için de geçerlidir. Her kullanımdan önce neden her işlevi yeniden değil? Benim cevabım: her şeyi ılımlılık içinde yapın. Çok fazla dağınıklık okunabilirliği engeller (aynı zamanda sürdürülebilirlik --- fonksiyonun imzasının değişmesi gerekir). Bu yüzden bir kural çıkarmayacak olsam da, fonksiyonları yerel olarak bildirmek bazen yararlıdır.

+0

Hiç yaptın mı? Bunu yapan birini tanıyor musun? Yapmıyorum ve bazı _real_world örneklerine bağlanabilirseniz harika olur. Bu gerçekten ilginç olurdu. – Tobias

+0

Gerçekten bir veya iki kez yapmama konusunda belirsiz bir hatırlatma var, ama belirli bir örneği hatırlamıyorum. Mesele şu ki, benim çalıştığım çoğu fonksiyon sınıf yöntemleri, bu yüzden oldukça nadir. – Ari

1

this answer numaralı 3 snippet'te belirtildiği gibi, kapsam gölgelendirmesi konusunda yardımcı olabilir.

#include <stdio.h> 

void c(); // Prototype of a function named ``c'' 

int main() { 

    c(); // call the function 

    { // additional scoppe needed for C, not for C++ 
     int c = 0; // shadow the c function with a c integer variable 
     c++; 
     { 
      void c(); // hide the c integer variable back with the c function 
      c(); 
     } 
     ++c; 
    } //! 

    return 0; 
} 

void c() { 
    printf("Called\n"); 
} 

bu kullanımı genellikle yararlı olması muhtemeldir sanmıyorum ve herhangi iyi tasarlanmış bir program ortaya çıkmıyordu şansı vardır.

Yine de bu özellik için en olası neden olduğunu düşünüyorum, son zamanlarda değişkenler gibi işlevler tanımlamak sadece doğru değil.

İlgili konular