Neden iki madde, gcc'de aynı şekilde görülen aynı türden çözülüyor?


32

Cümleleri kullanarak iki temel sınıfım var

 class MultiCmdQueueCallback {
  using NetworkPacket  = Networking::NetworkPacket;
  ....
 }


 class PlcMsgFactoryImplCallback {
   using NetworkPacket = Networking::NetworkPacket;
  ....
 }

Sonra bir sınıf beyan ederim

class PlcNetwork : 
  public RouterCallback, 
  public PlcMsgFactoryImplCallback, 
  public MultiCmdQueueCallback {
  private:
    void sendNetworkPacket(const NetworkPacket &pdu);
}

derleyici daha sonra 'NetworkPacket' için bir hata başvurusu belirsiz 'sendNetworkPacket (NetworkPacket & ...'

Şimdi her ikisi de 'yan tümceleri' aynı temel sınıfa gider Ağ: NetworkPacket

ve aslında yöntem beyanını yerine koyarsam:

 void sendNetworkPacket(const Networking::NetworkPacket &pdu);

iyi derler.

Derleyici, her ikisi de aynı temel türü işaret etseler bile, her bir yan tümcesi ayrı bir tür olarak ele alıyor. Bu standart tarafından zorunlu mu yoksa derleyici hatası var mı?


Derleyici yeterince zeki görünmüyor
idris

Bu noktada derleyici olan nokta sadece üç tane olduğunu bilir NetworkPacket- MultiCmdQueueCallback'te, PlcMsgFactoryImplCallback'te, Networking'de. Hangisinin kullanılacağı belirtilmelidir. Ve burada koymanın virtualherhangi bir yardımı olacağını sanmıyorum .
theWiseBro

@idris: bunun yerine, standardın yeterli izin vermediğini söylediniz. derleyiciler standardı takip etme hakkına sahiptir.
Jarod42

@ Jarod42 Aşağıda 'type-id ile gösterilen türün eş anlamlısı' cevabını verir, böylece aynı type-id'lere sahiplerse her ikisini de kullanabilirler. ister standart ister derleyici olsun, sadece birileri yeterince zeki görünmüyor.
idris

çoklu miras sorunlarından biri
eagle275

Yanıtlar:


28

Takma ad türüne (ve erişilebilirliğe) bakmadan önce

isimlere bakıyoruz

ve gerçekten,

NetworkPacket olabilir

  • MultiCmdQueueCallback::NetworkPacket
  • veya PlcMsgFactoryImplCallback::NetworkPacket

Her ikisinin de işaret ettiği gerçeği Networking::NetworkPacketönemsizdir.

Belirsizlikle sonuçlanan ad çözümlemesi yapıyoruz.


Aslında bu sadece kısmen doğruysa PlcNetwork için bir kullanım eklerseniz: | using NetworkPacket = MultiCmdQueueCallback :: NetworkPacket; Önceki kullanma yan tümcesi özel olduğundan derleyici hatası alıyorum.
Andrew Goedhart

@AndrewGoedhart Bir çelişki değil. İsim araması önce kendi sınıfında başlar. Derleyici daha sonra orada benzersiz bir isim bulursa, tatmin olur.
Aconcagua

Buradaki sorunum, adın neden temel sınıftaki özel adlandırma maddesinden kaynaklandığıdır. Özel bildirimlerden birini kaldırırsam, bu nedenle temel sınıflardan birinin yan tümcesi kullanarak özel bir özelliği vardır ve diğer hiçbiri yoksa, hata 'Ağ Paketi bir tür belirtmez' olarak değişir
Andrew Goedhart

1
@AndrewGoedhart Ad araması (açıkça) erişilebilirliği dikkate almaz. Birini genel diğerini özel yaparsanız aynı hatayı alırsınız. Bu keşfedilen ilk hata, bu yüzden yazdırılacak ilk hata bu. Bir takma adı kaldırırsanız, belirsizlik sorunu ortadan kalkar, ancak erişilemezlik durumu kalır, böylece bir sonraki hatayı yazdırırsınız. Bu arada, iyi bir hata mesajı (? MSVC kez daha), GCC daha konusunda daha doğrudur: error: [...] is private within this context.
Aconcagua

1
@AndrewGoedhart Aşağıdakileri göz önünde bulundurun: class A { public: void f(char, int) { } private: void f(int, char) { } }; void demo() { A a; a.f('a', 'd'); }- aynı değil, ancak aşırı yük çözünürlüğü aynı şekilde çalışır: Tüm uygun işlevleri düşünün, ancak uygun olanı erişilebilirliği düşünün seçtikten sonra düşünün ... privat işlevini iki karakter kabul edecek şekilde değiştirirseniz, özel olsa da seçilir - ve bir sonraki derleme hatasıyla karşılaşırsınız.
Aconcagua

14

Hangisini kullanmak istediğinizi manuel olarak seçerek belirsizliği çözebilirsiniz.

class PlcNetwork : 
  public RouterCallback, 
  public PlcMsgFactoryImplCallback, 
  public MultiCmdQueueCallback {

using NetworkPacket= PlcMsgFactoryImplCallback::NetworkPacket; // <<< add this line
private:
    void sendNetworkPacket(const NetworkPacket &pdu);

}

Derleyici yalnızca temel sınıflardaki tanımları arar. Her iki temel sınıfta da aynı tür ve / veya takma ad varsa, hangisinin kullanılacağını bilmediğinden şikayet eder. Ortaya çıkan türün aynı olup olmadığı önemli değildir.

Derleyici yalnızca ilk adımda adları arar, bu ad bir işlev, tür, diğer ad, yöntem veya başka bir şeyse tamamen bağımsızdır. İsimler belirsizse derleyiciden başka bir işlem yapılmaz! Sadece hata mesajı ile şikayet eder ve durur. Bu nedenle, verilen ifadeyle belirsizliği çözün.


İfadeler hakkında bazı şüpheleriniz var. Tanımlara bakarsa , türü de dikkate almaz mı? Sadece isimleri aramakla kalmaz (ve nasıl tanımlandığını da unutmaz)? Standart bazı referans harika olurdu ...
Aconcagua

Bu son yorum nedenini doğru bir şekilde açıklıyor . Son paragrafı bu yorumla değiştir ve ben oy vereceğim;)
Aconcagua

Kabul edemem - soru yazarı değilim ... Sinirlerinize kapılmış olsaydım üzgünüm. Sadece cevabı geliştirmeye çalışıyorum, daha önce
KG'nin

@Aconcagua: Ubs, benim hatam :-) İyileştirdiğiniz için teşekkürler!
Klaus

Aslında bu işe yaramaz çünkü her iki kullanma cümlesi de özeldir. PlcNetwork için bir kullanarak eklerseniz: | using NetworkPacket = MultiCmdQueueCallback :: NetworkPacket; Önceki kullanma yan tümcesi özel olduğundan derleyici hatası alıyorum. Bu arada, bir temel sınıfı yan tümceyi genel ve diğerini özel kullanarak yaparsam, yine de bir belirsizlik hatası alıyorum. Temel sınıfta tanımlı olmayan yöntemlerde belirsizlik hataları alıyorum.
Andrew Goedhart

8

Gönderen docs :

Tür takma adı bildirimi, type-id ile gösterilen türün eş anlamlısı olarak kullanılabilecek bir ad sunar. Yeni bir tür oluşturmaz ve varolan bir tür adının anlamını değiştiremez.

Bu iki usingcümle aynı türü temsil etse de , derleyici aşağıdaki durumda iki seçeneğe sahiptir:

void sendNetworkPacket(const NetworkPacket &pdu);

Arasında seçim yapabilirsiniz:

  • MultiCmdQueueCallback::NetworkPacket ve
  • PlcMsgFactoryImplCallback::NetworkPacket

çünkü her iki sınıftan MultiCmdQueueCallbackve PlcMsgFactoryImplCallbacktemel sınıflardan miras alır . Derleyicinin ad çözümlemesinin bir sonucu, sahip olduğunuz belirsizlik hatasıdır. Bunu düzeltmek için derleyiciye şu şekilde birini veya başka birini kullanması gerektiğini açıkça bildirmeniz gerekir:

void sendNetworkPacket(const MultiCmdQueueCallback::NetworkPacket &pdu);

veya

void sendNetworkPacket(const PlcMsgFactoryImplCallback::NetworkPacket &pdu);

Dürüst olmak gerekirse, kendimi tatmin hissetmiyorum ... İkisi de aynı tiple eşanlamlıdır. Kolayca sahip olabilirim class C { void f(uint32_t); }; void C::f(unsigned int) { }(takma ad eşleşmeleri sağlanan). Öyleyse neden burada bir fark var? Onlar hala aynı tip, senin atıf tarafından teyit (ki ben açıklamak için yeterli olduğunu düşünmüyorum) ...
Aconcagua

@Aconcagua: Taban türünü veya takma adı kullanmak asla fark etmez. Takma ad hiçbir zaman yeni bir tür değildir. Gözleminizin, iki temel sınıfta AYNI takma ad vererek oluşturduğunuz belirsizlikle ilgisi yoktur.
Klaus

1
@Aconcagua Sanırım bahsettiğiniz örnek sorudan durum için doğru eşdeğer değil
NutCracker

Biraz değiştirelim: A, B ve C sınıflarını ve typedef D'yi adlandıralım, o zaman bile yapabilirsiniz: class C : public A, public B { void f(A::D); }; void C::f(B::D) { }- en azından GCC kabul eder.
Aconcagua

Soru yazarı, kelimenin tam anlamıyla 'Derleyici, her ikisi de aynı temel türü işaret etseler bile, her bir maddeyi neden ayrı bir tür olarak ele alıyor?' - ve atıfın neden açıklığa kavuştuğunu görmüyorum , bunun yerine sadece KG'nin karışıklığını onaylıyor ... Cevabın yanlış olduğunu söylemek istemiyorum , ama gözlerimde yeterince netleşmiyor .. .
Aconcagua

2

İki hata var:

  1. Özel tür takma adlarına erişme
  2. Tür takma adlarına belirsiz referans

Özel-özel

Derleyici ikinci sorun hakkında şikayetçi bir sorun görmüyorum çünkü sipariş gerçekten önemli değil - devam etmek için her iki sorunu da düzeltmeniz gerekiyor.

Kamu-kamu

Her ikisinin görünürlüğünü MultiCmdQueueCallback::NetworkPacketve PlcMsgFactoryImplCallback::NetworkPacketgenel veya korumalı olarak değiştirirseniz, ikinci sorun (belirsizlik) açıktır - bunlar aynı temel veri türüne sahip olmalarına rağmen iki farklı tür takma adıdır. Bazıları "akıllı" bir derleyicinin bunu (belirli bir durumu) sizin için çözebileceğini düşünebilir, ancak derleyicinin duruma özel istisnalar yapmak yerine "genel olarak düşünmesi" ve küresel kurallara dayalı kararlar alması gerektiğini unutmayın. Aşağıdaki durumu düşünün:

class MultiCmdQueueCallback {
    using NetworkPacketID  = size_t;
    // ...
};


class PlcMsgFactoryImplCallback {
    using NetworkPacketID = uint64_t;
    // ...
};

Derleyici her ikisine NetworkPacketIDde aynı şekilde mi davranmalıdır ? Elbette değil. Çünkü 32 bit sistemde size_t32 bit uzunluğunda uint64_t, her zaman 64 bit uzunluğundadır . Ancak derleyicinin altta yatan veri türlerini kontrol etmesini istiyorsak, 64 bit sistemde bunları ayırt edemedi.

kamu-özel

Bu örneğin OP'nin kullanım senaryosunda bir anlam ifade etmediğine inanıyorum, ancak burada genel olarak sorunları çözdüğümüz için şunu düşünelim:

class MultiCmdQueueCallback {
private:
    using NetworkPacket  = Networking::NetworkPacket;
    // ...
};

class PlcMsgFactoryImplCallback {
public:
    using NetworkPacket  = Networking::NetworkPacket;
    // ...
};

Ben derleyici davranmalı bu durumda düşünmek PlcNetwork::NetworkPacketolarak PlcMsgFactoryImplCallback::NetworkPacketo başka hiçbir choises vardır çünkü. Neden hala bunu reddediyor ve belirsizlikle ilgili suçlamalar benim için bir mistery.


"Neden hala bunu reddediyor ve belirsizlikle ilgili suçlamalar benim için bir puslu." C ++ 'da, ad arama (görünürlük) erişim denetiminden önce gelir. IIRC, bir yerde okumak için gerekçe özel bir isim kamuya bir isim değiştirmek mevcut kodu kırmak değil, ama tamamen emin değilim.
LF
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.