Зіткнення простору імен 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, він працює.

Моє запитання - в чому полягає раціональність цієї поведінки (я вважаю, що це має відношення до того, що успадкування змушує Бар увійти до простору імен A, але я не можу знайти жодної документації, яка б підтверджувала цю теорію.


8
Я думаю, що це пов'язано з пошуку аргументів. Я позначив "мовний юрист", оскільки я вважаю, що ви шукаєте відповіді, які посилаються на мовний стандарт. І дуже хороше перше запитання! Це робить все варте.
Вірсавія

Він не входить у простір імен A, який ви можете бачити, якщо ви дозволите Barуспадкувати іншу структуру в A. Тоді двозначності немає. Це більше схоже на спадкування додає все від A::Fooв Barтому числі дозвіл Fooна A::Foo. Вибачте, я не можу реально висловити це більш точно.
n314159

@Bathsheba Ви маєте на увазі пошук типу аргументів для пошуку імен функцій (або імен шаблонів функцій) або залежних імен у шаблонах?
curiousguy

Відповіді:


22

Кожен клас має вводити його ім'я як члена. Тож можна назвати A::Foo::Foo. Це називається введеним ім'ям класу.

[клас]

2 Ім'я класу вставляється в область, в якій воно оголошується одразу після побачення імені класу. Ім'я класу також вставляється в область самого класу; це відомо як ім'я введеного класу. У цілях перевірки доступу ім'я введеного класу трактується так, ніби це ім'я загальнодоступного члена.

[basic.lookup]

3 Ім'я введеного класу класу також вважається членом цього класу з метою приховування імен і пошуку імені.

Оскільки в області класу починається некваліфікований пошук імені типу аргументу Bar, він продовжить потрапляти в область його базового класу для обліку будь-якого члена там. І воно знайдеться A::Foo::Fooяк назва типу.

Якщо ви хочете використовувати ім'я глобального типу, просто кваліфікуйте його за оточуючим (глобальним) простором імен.

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

Це робить повністю кваліфікований пошук у тій області, де ім'я введеного класу не відображається.

Для подальшого питання "чому" див


5
@ TedLyngmo - ADL відбувається з викликами функцій, нічого важливого в цих конкретних уривках.
StoryTeller - Невідповідна Моніка

Окі, я читав і не був впевнений. Дякую!
Тед Лінгмо

3
Це призводить до дуже кумедних, struct Bar:: A::Foo::Foo::Foo::Foo::Foo {}; але є контексти, де A::Foo::Fooпозначається конструктор, і, таким чином, там ви не можете продовжувати додавати стільки, Fooскільки хочете. Це схоже (але з абсолютно інший механізм) з тим , що ви можете викликати функцію fнаступним чином: (************f)().
AProgrammer

@AProgrammer - Дійсно. І можна побудувати ще кумедніші приклади .
StoryTeller - Невідповідна Моніка

Ця відповідь, безумовно, пояснює "що". Чи можна вдосконалити додавання "чому"? Як у, яка мета цього правила? У яких випадках використання це покращується чи стає можливим?
davidbak

2

Не повна відповідь, лише код, який показує (оскільки він компілюється), що Barне входить до namespace A. Ви можете бачити, що при успадкуванні від A::Foo1не виникає проблем, неоднозначність Fooяких було б інакше, якби це спадкування дозволило Barвступити 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;
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.