C ++ namespace collision dalam copy constructor

33

Saya memiliki kode berikut:

namespace A {
    struct Foo {
        int a;
    };
}

struct Foo {
    int b;
};

struct Bar : public A::Foo {
    Bar(Foo foo) {
        c = foo.b;
    }
    int c;
};

Kompiler C ++ mengeluh pada "c = foo.b" karena A :: Foo tidak memiliki anggota bernama b. Jika saya mengubah jenis parameter Bar dengan :: Foo berfungsi.

Pertanyaan saya adalah apa rasional di balik perilaku ini (saya kira itu ada hubungannya dengan fakta bahwa warisan membuat Bar memasuki ruang nama A tetapi saya tidak dapat menemukan dokumentasi untuk mendukung teori ini.

Vincent Le Ligeour
sumber
8
Saya pikir ini terkait pencarian argumen terkait. Saya telah menandai "pengacara bahasa" karena saya pikir Anda mencari jawaban yang merujuk pada standar bahasa. Dan pertanyaan pertama yang sangat bagus! Membuat semuanya bermanfaat.
Batsyeba
Itu tidak memasuki namespace A, yang dapat Anda lihat jika Anda membiarkan Barmewarisi dari struct lain di A. Maka tidak ada ambiguitas. Hal ini lebih seperti warisan menambahkan segala sesuatu dari A::Fooke Bartermasuk resolusi Foountuk A::Foo. Maaf, saya tidak bisa mengungkapkannya dengan lebih tepat.
n314159
@Bathsheba Apakah maksud Anda pencarian tipe nama tipe argumen untuk menemukan nama fungsi (atau nama templat fungsi) atau nama dependen dalam templat?
curiousguy

Jawaban:

22

Setiap kelas menyuntikkan namanya sebagai anggota. Jadi Anda bisa memberi nama A::Foo::Foo. Ini disebut nama kelas yang disuntikkan.

[kelas]

2 Nama kelas dimasukkan ke dalam lingkup yang dideklarasikan segera setelah nama kelas terlihat. Nama kelas juga dimasukkan ke dalam ruang lingkup kelas itu sendiri; ini dikenal dengan nama kelas yang disuntikkan. Untuk keperluan pemeriksaan akses, nama kelas yang disuntikkan diperlakukan seolah-olah itu adalah nama anggota publik.

[basic.lookup]

3 Nama kelas yang disuntikkan kelas juga dianggap sebagai anggota kelas itu untuk tujuan menyembunyikan nama dan pencarian.

Karena pencarian nama yang tidak memenuhi syarat dari jenis argumen dimulai dalam lingkup kelas Bar, itu akan berlanjut ke dalam ruang lingkup kelas dasarnya untuk memperhitungkan setiap anggota di sana. Dan itu akan ditemukan A::Foo::Foosebagai nama tipe.

Jika Anda ingin menggunakan nama tipe global, cukup kualifikasi dengan namespace sekitarnya (global).

Bar(::Foo foo) {
    c = foo.b;
}

Yang melakukan pencarian yang memenuhi syarat dalam lingkup di mana nama kelas yang disuntikkan tidak muncul.

Untuk pertanyaan "mengapa" tindak lanjut, lihat

StoryTeller - Unslander Monica
sumber
5
@TedLyngmo - ADL terjadi dengan panggilan fungsi, tidak ada yang relevan dalam bagian-bagian tertentu.
StoryTeller - Unslander Monica
Oki, saya sedang membaca dan tidak yakin. Terima kasih!
Ted Lyngmo
3
Ini mengarah ke hal yang sangat lucu struct Bar:: A::Foo::Foo::Foo::Foo::Foo {}; tetapi ada konteks di mana A::Foo::Foomenunjuk konstruktor dan dengan demikian di sana Anda tidak dapat terus menambahkan sebanyak yang FooAnda inginkan. Ini mirip (tapi dengan mekanisme yang sama sekali berbeda) dengan fakta bahwa Anda dapat memanggil fungsi fseperti ini: (************f)().
Pemrogram
@AProgrammer - Memang. Dan orang dapat membuat lebih banyak contoh lucu .
StoryTeller - Unslander Monica
Jawaban ini tentu menjelaskan "apa". Bisakah ditingkatkan untuk menambahkan "mengapa"? Seperti dalam, apa tujuan dari aturan ini? Kasus penggunaan mana yang diperbaiki atau dimungkinkan?
davidbak
2

Bukan jawaban yang lengkap, hanya kode yang menunjukkan (karena mengkompilasi) yang Bartidak memasukkan namespace A. Anda dapat melihat bahwa ketika mewarisi dari A::Foo1sana tidak ada masalah dengan ambiguitas Fooyang akan berbeda jika warisan ini memungkinkan Barmasuk A.

namespace A {
    struct Foo {
        int a;
    };

    struct Foo1 {
        int a;
    };
}

struct Foo {
    int b;
};

struct Bar : public A::Foo1 {
    Bar(Foo foo) {
        c = foo.b;
    }
    int c;
};

n314159
sumber