การชนกันของเนมสเปซ C ++ ในตัวสร้างสำเนา


33

ฉันมีรหัสต่อไปนี้:

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

struct Foo {
    int b;
};

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

คอมไพเลอร์ C ++ บ่นที่ "c = foo.b" เนื่องจาก A :: Foo ไม่มีสมาชิกชื่อ b ถ้าฉันเปลี่ยนประเภทของพารามิเตอร์ Bar ด้วย :: Foo มันใช้งานได้

คำถามของฉันคือเหตุผลที่อยู่เบื้องหลังพฤติกรรมนี้ (ฉันคิดว่ามันเกี่ยวข้องกับข้อเท็จจริงที่ว่าการสืบทอดทำให้ Bar ป้อนเนมสเปซ A แต่ฉันไม่พบเอกสารใด ๆ เพื่อสนับสนุนทฤษฎีนี้


8
ฉันคิดว่ามันเกี่ยวข้องกับการค้นหาการโต้แย้ง ฉันติดแท็ก "language-Lawyers" เนื่องจากฉันคิดว่าคุณหลังจากได้รับคำตอบที่อ้างอิงมาตรฐานภาษา และเป็นคำถามแรกที่ดีมาก! ทำให้ทุกอย่างคุ้มค่า
Bathsheba

มันไม่ได้ใส่เนมสเปซAซึ่งคุณสามารถดูว่าคุณปล่อยให้Barสืบทอดจากโครงสร้างอื่นAหรือไม่ จากนั้นไม่มีความกำกวม มันเป็นเหมือนมรดกเพิ่มทุกอย่างจากA::FooการBarรวมทั้งความละเอียดของการFoo A::Fooขออภัยฉันไม่สามารถแสดงได้อย่างแม่นยำมากขึ้น
n314159

@Bathsheba คุณหมายถึงการค้นหาชื่อที่ขึ้นอยู่กับชนิดของอาร์กิวเมนต์เพื่อค้นหาชื่อฟังก์ชัน (หรือชื่อเทมเพลตฟังก์ชัน) หรือชื่อที่พึ่งพาในเทมเพลตหรือไม่
curiousguy

คำตอบ:


22

ทุกชั้นมีชื่อของมันถูกฉีดเข้าไปในฐานะสมาชิก A::Foo::Fooดังนั้นคุณสามารถตั้งชื่อ สิ่งนี้เรียกว่าชื่อคลาสที่ถูกฉีด

[ระดับ]

2ชื่อคลาสถูกแทรกลงในขอบเขตที่ประกาศทันทีหลังจากเห็นชื่อคลาส class-name ถูกแทรกเข้าไปในขอบเขตของคลาสเอง สิ่งนี้เป็นที่รู้จักกันในชื่อฉีด - คลาส - ชื่อ สำหรับวัตถุประสงค์ในการตรวจสอบการเข้าถึงชื่อที่ถูกแทรกคลาสจะได้รับการปฏิบัติเสมือนเป็นชื่อสมาชิกสาธารณะ

[basic.lookup]

3ชื่อคลาสที่ถูกแทรกของคลาสนั้นยังถือว่าเป็นสมาชิกของคลาสนั้นเพื่อวัตถุประสงค์ในการซ่อนชื่อและค้นหา

เนื่องจากการค้นหาชื่ออย่างไม่มีเงื่อนไขของชนิดอาร์กิวเมนต์เริ่มต้นในขอบเขตของคลาสBarจึงจะดำเนินการต่อในขอบเขตของคลาสพื้นฐานเพื่อบัญชีสมาชิกใด ๆ และมันจะค้นหาA::Foo::Fooเป็นชื่อประเภท

หากคุณต้องการใช้ชื่อประเภทโกลบอลเพียงแค่ผ่านการรับรองโดยเนมสเปซที่ล้อมรอบ (ทั่วโลก)

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

ซึ่งกำลังทำการค้นหาที่ผ่านการรับรองโดยสมบูรณ์ในขอบเขตที่ชื่อคลาสที่ถูกแทรกไม่ปรากฏขึ้น

สำหรับการติดตามคำถาม "ทำไม" ดู


5
@TedLyngmo - ADL เกิดขึ้นกับการเรียกใช้ฟังก์ชันไม่มีสิ่งใดที่เกี่ยวข้องกับข้อความเฉพาะเหล่านั้น
StoryTeller - Unslander Monica

โอกิฉันกำลังอ่านหนังสือและไม่แน่ใจ ขอบคุณ!
Ted Lyngmo

3
สิ่งนี้นำไปสู่ความสนุกมากstruct Bar:: A::Foo::Foo::Foo::Foo::Foo {}; แต่มีบริบทที่A::Foo::Fooกำหนดตัวสร้างและทำให้คุณไม่สามารถเพิ่มได้มากFooเท่าที่คุณต้องการ นี้จะคล้าย ( แต่ด้วยกลไกที่แตกต่างกันอย่างสิ้นเชิง) ความจริงที่ว่าคุณสามารถเรียกฟังก์ชั่นด้วยวิธีนี้:f (************f)()
AProgrammer

@AProgrammer - แน่นอน และหนึ่งสามารถสร้างแม้กระทั่งตัวอย่างสนุกมากขึ้น
StoryTeller - Unslander Monica

คำตอบนี้จะอธิบายถึง "อะไร" อย่างแน่นอน สามารถปรับปรุงเพื่อเพิ่ม "ทำไม" ได้หรือไม่ ในขณะที่วัตถุประสงค์ของกฎนี้คืออะไร? กรณีใดบ้างที่ใช้ปรับปรุงหรือทำให้เป็นไปได้
davidbak

2

ไม่ได้เป็นคำตอบที่สมบูรณ์รหัสเดียวที่แสดงให้เห็น (เพราะมันรวบรวม) ที่ไม่ได้ใส่Bar namespace Aคุณจะเห็นว่าเมื่อมีการสืบทอดมาจากA::Foo1มีปัญหาใด ๆ กับความคลุมเครือของFooซึ่งจะแตกต่างกันถ้าได้รับมรดกนี้จะช่วยให้ใส่BarA

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;
};
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.