class A
{
private:
int a;
public:
class B
{
public:
B(int a) : b(a) {}
int b;
};
};
int main(void)
{
return 0;
}
clang (-Weverything) предупреждает:
t.cpp(10,15): warning: constructor parameter 'a' shadows the field 'a' of 'A' [-Wshadow-field-in-constructor]
10 | B(int a) : b(a)
| ^
t.cpp(4,9): note: previous declaration is here
4 | int a;
Я знаю, что, поскольку вложенные классы C++11 имеют доступ к внешним классам, как если бы они были друзьями, но B просто объявлен внутри A (в B нет объекта-члена A, как может B параметр конструктора a тень A член a ?
для поиска имени не имеет значения, что в A нет экземпляра B, A::a все равно находится. вложенные классы странные, я стараюсь их избегать
¯\_(ツ)_/¯ Кланг не ошибается. Вы можете перепутать параметр с членом класса, даже если он является членом включающего класса и недоступен.





Затеняет ли параметр конструктора вложенного класса члены включающего класса?
Да.
Для поиска имени не требуется экземпляр A в B. Поскольку B вложено внутрь A, неквалифицированный поиск имени находит A::a.
Из cppreference:
Для имени, используемого в любом месте определения класса (включая спецификаторы базового класса и определения вложенных классов), за исключением тела функции-члена, аргумента по умолчанию функции-члена, спецификации исключения функции-члена или инициализатора члена по умолчанию, где член может принадлежат вложенному классу, определение которого находится в теле включающего класса, поиск осуществляется в следующих областях:
а) тело класса, в котором имя используется до момента использования,
б) все тело его базового класса(ов), рекурсивно обращающееся к их базам, когда объявления не найдены,
в) если этот класс вложен, тело включающего класса до определения этого класса и все тело базового класса(ов) включающего класса,
[...]
в) означает, что неквалифицированный незатененный a внутри B относится к A::a. Ваш код работает, потому что int a тени A::a и потому что в списке инициализаторов b(a) используется параметр конструктора с именем a.
То, что включающий класс является другом, на данном этапе не имеет значения, поскольку доступ осуществляется после поиска имени. Как отметил Jarod42, вы можете изменить код (переименовать параметр, но сохранить b(a)), чтобы получить ошибку, поскольку A::a не статичен.
Спасибо Artyer за пример, где член a действительно можно использовать для чего-то:
struct A {
int a;
struct B {
public:
decltype(a) b; // equivalent to decltype(A::a) b;
B(int a) : b(a) {} // int a shadows A::a
// equivalent to B(int x) : b(x) {}
};
};
Мне было бы любопытно увидеть пример, где неквалифицированный поиск имени приводит к A::a, как в коде OP, и его действительно можно использовать без ошибок. мне не удалось найти ни одного
«означает, что неквалифицированное (незатененное) a внутри B относится к A::a...» Нет, в этом примере неквалифицированное a внутри B относится к параметру конструктора a. Под (незатененной) частью вы, вероятно, имели в виду, что если бы параметр ctor не был назван так же, как элемент данных, то он ссылался бы на этот элемент.
ок, namelookup/shadow не подразумевает возможного использования, поэтому предупреждение правильное (хотя иногда и раздражающее)
@albert Да, предупреждение верное.
@463035818_is_not_an_ai decltype может использовать нестатические элементы данных без уточнения имени класса. Например, class B { decltype(a) b; } находит A::a и decltype(a) -> decltype(A::a) -> int: godbolt.org/z/E6PfYGbP5
@user12002570 user12002570 Я переосмыслил себя, чтобы больше не обращать внимания на ваши комментарии. Вы можете удалить их. Или вы можете оставить их здесь. Хотя без контекста (моих комментариев) они проливают довольно искаженный свет на этот вопрос.
Если вы прокомментируете
aвB, все составители согласятся, что вы ссылаетесь наA::aДемо (что неверно, поскольку неstatic), поэтому они согласны с тем, чтоint aтениA::a.