Türetilmiş sınıfta aynı ada ancak farklı imzaya sahip işlev


92

Aynı ada sahip, ancak temel ve türetilmiş sınıflarda farklı imzaya sahip bir işlevim var. Türetilmiş sınıftan miras alan başka bir sınıfta temel sınıfın işlevini kullanmaya çalıştığımda bir hata alıyorum. Aşağıdaki koda bakın:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

Gcc derleyicisinden şu hatayı alıyorum:

In member function `void C::bar()': no matching function for call to `C::foo(std::string&)' candidates are: int B::foo(int)

Sınıftan çıkarırsam veya int foo(int i){};sınıftan Byeniden adlandırırsam foo1, her şey yolunda gider.

Bununla ilgili sorun nedir?


1
Teknik olarak bu sorunun bir kopyası ama bunun daha iyi bir başlığı ve cevapları var.
Troubadour

Yanıtlar:


79

Temel sınıflardaki işlevleri geçersiz kılmayan, ancak aynı ada sahip türetilmiş sınıflardaki işlevler, temel sınıftaki aynı ada sahip diğer işlevleri gizleyecektir .

Gördüğünüz şey genellikle arzu edilen bir davranış olmadığından, bas sınıfındaki işlevlerle aynı adı taşıyan ve temel sınıf işlevlerini geçersiz kılması amaçlanmayan türetilmiş sınıflarda işlevlere sahip olmak genellikle kötü bir uygulama olarak kabul edilir. Genellikle farklı işlevlere farklı adlar verilmesi tercih edilir.

Temel işlevi aramanız gerekirse, tuşunu kullanarak aramanın kapsamını belirlemeniz gerekecektir A::foo(s). Bunun aynı zamanda herhangi bir sanal işlev mekanizmasını da devre dışı bırakacağını unutmayın A::foo(string).


13
litdb'nin cevabını da okuyun: B'deki 'A :: foo' cümlesini kullanarak temel işlevi 'gösterebilirsiniz'.
xtofl

Doğru, arama sitesinde kullanılabilecek ve temel hiyerarşiyi sabit olarak ele alan bir çözüme bakıyordum.
CB Bailey

2
Bu iddianın temeli nedir ve ardından gelen tavsiye şu şekildedir : " Türetilmiş sınıflarda, bas sınıfındaki işlevlerle aynı adı taşıyan ve temel sınıf işlevlerini geçersiz kılma amacı taşımayan işlevlere sahip olmak genellikle kötü bir uygulama olarak kabul edilir. Gördüğünüz şey genellikle arzu edilen bir davranış değildir. Genellikle farklı işlevlere farklı adlar vermek tercih edilir " . Ya anlamsal olarak aynı şeyi yapıyorlarsa? C ++, Johannes'in cevabında açıklandığı gibi, bunun neden olduğu soruna bir çözüm sağlar.
Nawaz

109

Bunun nedeni, üslerinizden birinde bir ad bulursa ad aramanın durmasıdır. Diğer üslerde ötesine bakmayacak. B'deki işlev, A'daki işlevi gölgeler . A'nın işlevini B kapsamında yeniden bildirmeniz gerekir, böylece her iki işlev de B ve C içinden görülebilir:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
    using A::foo;
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

Düzenleme: Standardın verdiği gerçek açıklama (10.2 / 2'den itibaren):

Aşağıdaki adımlar, bir sınıf kapsamındaki ad aramasının sonucunu tanımlar, C. İlk olarak, sınıftaki ve temel sınıf alt nesnelerinin her birindeki ad için her bildirim dikkate alınır. Bir alt nesne B'deki bir üye adı f, eğer A, B'nin temel sınıf alt nesnesiyse, bir alt nesnede f üye adını gizler. Bir kullanma bildirimi ile sunulan bu bildirimlerin her biri, kullanım bildirimi ile belirtilen bildirimi içeren türdeki C'nin her bir alt nesnesinden olduğu kabul edilir. 96) Sonuçta ortaya çıkan bildirimler hepsi aynı türden alt nesnelerden veya setin statik olmayan bir üyesi var ve farklı alt nesnelerden üyeler içeriyor, bir belirsizlik var ve program kötü biçimlendirilmiş. Aksi takdirde, bu küme aramanın sonucudur.

Başka bir yerde (hemen üstünde) söyleyecekleri şudur:

Bir id ifadesi için [ "foo" gibi bir şey ], bunun sınıf kapsamında ad araması başlar; nitelenmiş bir kimlik için [ "A :: foo" gibi bir şey, A iç içe geçmiş bir ad belirticidir ], ad araması, iç içe geçmiş ad belirticisi kapsamında başlar. Ad araması, erişim kontrolünden önce gerçekleşir (3.4, madde 11).

([...] benim tarafımdan belirlendi). Bunun, B'deki foo'nuz özel olsa bile, A'daki foo'nun hala bulunmayacağı anlamına gelir (çünkü erişim denetimi daha sonra gerçekleşir).


litb, cevabın için teşekkürler. Ama kodunuzu derlemeye çalıştığımda void A::foo(class basic_string<char,char_traits<char>,allocator<char> >)' in , aynı isimli int B :: foo (int) 'yerel metodu nedeniyle B sınıfına erişimi ayarlayamıyorum . Belki de
gcc'nin

1
evet, kesinlikle bir derleyici hatası. eski derleyiciler "A :: foo;" kullanırlardı "using A :: foo;" yerine ancak ilki C ++ 'da kullanımdan kaldırılmıştır.
Johannes Schaub - litb
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.