Xung đột không gian tên C ++ trong constructor sao chép


33

Tôi có đoạn mã sau:

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

struct Foo {
    int b;
};

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

Trình biên dịch C ++ phàn nàn tại "c = foo.b" vì A :: Foo không có thành viên tên b. Nếu tôi thay đổi loại tham số Bar bằng :: Foo thì nó hoạt động.

Câu hỏi của tôi là lý do đằng sau hành vi này là gì (tôi cho rằng nó phải làm với thực tế là sự kế thừa làm cho Bar bước vào không gian tên A nhưng tôi không thể tìm thấy bất kỳ tài liệu nào để hỗ trợ lý thuyết này.


8
Tôi nghĩ rằng đó là đối số tra cứu phụ thuộc liên quan. Tôi đã gắn thẻ "luật sư ngôn ngữ" vì tôi nghĩ rằng bạn đang trả lời câu trả lời tham chiếu tiêu chuẩn ngôn ngữ. Và một câu hỏi đầu tiên rất hay! Làm cho tất cả có giá trị.
Bathsheba

Nó không nhập không gian tên A, mà bạn có thể thấy nếu bạn để Barkế thừa từ một cấu trúc khác trong A. Thế thì không có sự mơ hồ. Nó là giống như các thừa kế cho biết thêm tất cả mọi thứ từ A::Foođể Barbao gồm độ phân giải Footới A::Foo. Xin lỗi, tôi không thể thực sự diễn đạt nó chính xác hơn.
n314159

@Bathsheba Bạn có nghĩa là tra cứu tên phụ thuộc loại đối số để tìm tên hàm (hoặc tên mẫu hàm) hoặc tên phụ thuộc trong mẫu?
tò mò

Câu trả lời:


22

Mỗi lớp có tên của nó được thêm vào như một thành viên. Vì vậy, bạn có thể đặt tên A::Foo::Foo. Đây được gọi là tên lớp được tiêm.

[lớp học]

2 Một lớp học có tên tuổi được đưa vào phạm vi trong đó nó được tuyên bố ngay sau khi class-name được nhìn thấy. Tên lớp cũng được chèn vào phạm vi của chính lớp đó; cái này được gọi là tên lớp tiêm. Đối với mục đích kiểm tra truy cập, tên lớp được tiêm được xử lý như thể đó là tên thành viên công khai.

[cơ bản.lookup]

3 Tên lớp được tiêm của một lớp cũng được coi là thành viên của lớp đó với mục đích ẩn tên và tra cứu.

Bởi vì tra cứu tên không đủ tiêu chuẩn của loại đối số bắt đầu trong phạm vi của lớp Bar, nên nó sẽ tiếp tục vào phạm vi của lớp cơ sở của nó để giải thích cho bất kỳ thành viên nào ở đó. Và nó sẽ tìm thấy A::Foo::Foonhư một tên loại.

Nếu bạn muốn sử dụng tên loại toàn cầu, chỉ cần đủ điều kiện cho nó bằng không gian tên xung quanh (toàn cầu).

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

Đang thực hiện tra cứu đủ điều kiện trong phạm vi mà tên lớp được chèn không xuất hiện.

Để theo dõi câu hỏi "tại sao" xem


5
@TedLyngmo - ADL xảy ra với các lệnh gọi hàm, không có gì liên quan trong các đoạn cụ thể đó.
Người kể chuyện - Unslander Monica

Oki, tôi đã đọc và không chắc chắn. Cảm ơn!
Ted Lyngmo

3
Điều này dẫn đến rất thú vị struct Bar:: A::Foo::Foo::Foo::Foo::Foo {}; nhưng có những bối cảnh trong đó A::Foo::Foochỉ định nhà xây dựng và do đó bạn không thể tiếp tục thêm bao nhiêu Footùy thích. Điều này tương tự (nhưng với một cơ chế hoàn toàn khác) với thực tế là bạn có thể gọi một hàm ftheo cách này : (************f)().
AProgrammer

@AProgrammer - Thật vậy. Và người ta có thể xây dựng các ví dụ thú vị hơn nữa .
Người kể chuyện - Unslander Monica

Câu trả lời này chắc chắn giải thích "cái gì". Nó có thể được cải thiện để thêm "tại sao" không? Như trong, mục đích của quy tắc này là gì? Những trường hợp sử dụng nào nó cải thiện hoặc làm cho có thể?
davidbak

2

Không phải là một câu trả lời hoàn chỉnh, chỉ có mã hiển thị (vì nó biên dịch) Barkhông nhập namespace A. Bạn có thể thấy rằng khi kế thừa từ A::Foo1đó không có vấn đề gì với sự mơ hồ Foosẽ khác nếu kế thừa này cho phép Barnhập 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;
};
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.