Apakah Derived1 :: Base dan Derived2 :: Base merujuk ke tipe yang sama?

12

MSVC, Dentang dan GCC tidak setuju pada kode ini:

struct Base { int x; };
struct Der1 : public  Base {};
struct Der2 : public  Base {};

struct AllDer : public Der1, public Der2 {
    void foo() {
        Der1::Base::x = 5;
    }
};

Godbolt

GCC:

<source>: In member function 'void AllDer::foo()':    
<source>:10:21: error: 'Base' is an ambiguous base of 'AllDer'    
   10 |         Der1::Base::x = 5;    
      |                     ^    
Compiler returned: 1

Dentang memberikan kesalahan yang sama, dan MSVC tidak memberikan kesalahan.

Siapa di sini?

Saya kira ini tercakup dalam [class.member.lookup] , tetapi saya mengalami kesulitan memahami apa yang ingin saya sampaikan kepada saya untuk kasus ini. Silakan mengutip bagian-bagian yang relevan dan jika mungkin jelaskan dalam bahasa Inggris.

PS: Terinspirasi oleh pertanyaan ini. Mengapa Referensi ke Kelas Dasar ambigu dengan :: -operator melalui kelas turunan?

PPS: Sebenarnya keraguan saya adalah apakah Der1::Basemerujuk pada tipe, apakah itu Base(dan kemudian Der2::Baseadalah tipe yang sama persis), atau ke subobyek. Saya yakin itu yang pertama, tetapi jika ini yang terakhir maka MSVC akan benar.

idclev 463035818
sumber
@LanguageLawyer lebih banyak petunjuk bagus bahwa gcc dan dentang benar, tetapi saya tidak sepenuhnya yakin bahwa mscv salah
idclev 463035818
1
Jelas keduanya merujuk pada jenis yang sama ::Base, tetapi pertanyaan sebenarnya tampaknya sedikit berbeda di sini. Ada dua sub-jenis objek Base, dan keduanya memiliki Base::x anggota.
MSalters
@Mengganti PPS hanya mengungkapkan kebingungan saya. Jika Anda memiliki saran untuk judul yang lebih baik, jangan ragu untuk mengedit, saya sendiri tidak suka (karena saya tahu jawabannya adalah ya), tetapi tidak menemukan sesuatu yang lebih tepat
idclev 463035818
1
@ d4rk4ng31 tidak ada pewarisan virtual dalam kode (sengaja)
idclev 463035818

Jawaban:

6

Untuk menjawab pertanyaan dalam judul, ya, Derived1::Basereferensi nama kelas-disuntikkan [class.pre] Base dan begitu juga Derived2::Base. Keduanya merujuk ke kelas ::Base.

Sekarang, jika Baseakan memiliki statis anggota x, maka pencarian dari Base::xakan ambigu. Hanya ada satu.

Masalah dalam contoh ini adalah bahwa itu xadalah anggota non-statis, dan AllDermemiliki dua anggota tersebut. Anda dapat memisahkan akses semacam itu xdengan menetapkan kelas dasar yang tidak ambiguAllDer yang hanya memiliki satu xanggota. Derived1adalah kelas dasar yang tidak ambigu, dan memiliki satu xanggota, sehingga Derived1::xsecara jelas menentukan yang mana dari dua xanggota yang AllDerAnda maksud. Basejuga hanya memiliki satu xanggota, tetapi itu bukan basis yang jelas AllDer. Setiap instance dari AllDermemiliki dua sub-objek bertipe Base. Karena Base::xitu ambigu dalam contoh Anda. Dan karena Derived1::Basehanya nama lain untuk Baseini, ini tetap ambigu.

[class.member.lookup] menentukan yang xdicari dalam konteks specifier nama-bersarang, sehingga harus diselesaikan terlebih dahulu. Kami memang mencari Base::x, bukan Derived1::x, karena kami mulai dengan menetapkan Derived1::Basesebagai Base. Bagian ini berhasil, hanya ada satu xdi Base.Catatan 12 di [class.member.lookup] yang secara eksplisit memberi tahu Anda bahwa menggunakan pencarian nama yang tidak ambigu mungkin masih gagal ketika ada beberapa sub-proyek dengan nama yang sama. D::ipada contoh itu pada dasarnya adalah milik Anda Base::x.

MSalters
sumber
jadi itu hanya "mungkin gagal" tetapi tidak "harus gagal" dan ketiga penyusunnya benar? Pertimbangkan misalnya template <typname A,typename B> struct foo : A,Bdengan beberapa kode yang dibuat untuk mengakses anggota dari basis Adan B. Dalam kasus seperti itu saya ingin mendapatkan kesalahan
idclev 463035818
1
@ idclev463035818: Saya menduga itu adalah diagnostik "harus gagal" diperlukan . Secara khusus, ini gagal pada akses anggota Kelas 7.6.1.4/7, "tidak berbentuk".
MSalters
Saya harus membaca lebih banyak. Dan saya harus melakukan riset pada msvc, saya menduga bahwa beberapa flag diperlukan untuk mendapatkan hasil yang sepenuhnya sesuai, tetapi harus mencari tahu flag mana
idclev 463035818
2

Alasan Anda dapat merujuk ke nama kelas sebagai anggota kelas adalah karena cpp alias untuk penggunaan yang mudah, seolah-olah Anda menulis using Base = ::Base;di dalam Base.
Masalah yang Anda hadapi adalah bahwa Der1::Baseadalah Base.
Jadi, ketika Anda menulis Der1::Base::x, itu sama dengan Base::x.

Dani
sumber
Saya tahu apa "masalahnya" tetapi Anda tidak menjawab pertanyaan saya: "Siapa yang benar? (Dan mengapa?)"
idclev 463035818
@ idclev463035818: Saya percaya ini adalah sisi kanan. Bagian tentang usingditulis dalam standar, dan saya pikir itulah kunci untuk menafsirkan apa yang dikatakan ungkapan itu.
Dani
@ idclev463035818: Lihatlah contoh berikut. Saya pikir itu akan menghapusnya sedikit. ideone.com/IqpXjT
Dani
maaf tapi saya pikir Anda tidak mengerti maksud pertanyaan saya. Saya ingin tahu apa yang benar sesuai dengan standar, karena saya melihat kompiler berbeda melakukan hal yang berbeda. Melihat apa yang dilakukan oleh satu kompiler tertentu untuk satu contoh tertentu, tidak membantu menjawab pertanyaan
idclev 463035818
Hanya FYI, MSVC (ver 19.25.28614 untuk x64) gagal mengkompilasi contoh Anda di ideone.com/IqpXjT . Menggunakan cl /c /Wall /WX /Od /MDd /Za /permissive- /std:c++17 main.cppsebagai baris perintahnya, saya mendapatkanmain.cpp(7): error C2597: illegal reference to non-static member 'A::x'
heap underrun