Как работает поиск безусловного имени при использовании объявлений объявлений?

Это плохо или правильно сформировано в соответствии со стандартом c ++?

namespace M { struct i {}; }
namespace N { static int i = 1; }
using M::i;
using N::i;
int main() { sizeof (i); }

Clang отклоняет это, и GCC принимает это.

Согласно [namespace.udir-6] (http://eel.is/c++draft/basic.namespace#namespace.udir-6):

Если поиск имени находит объявление для имени в двух разных
пространства имен и объявления не объявляют одну и ту же сущность и делают
Не объявляйте функции, использование имени является неправильным.

Как мы должны интерпретировать это? Помните, что каждое объявление об использовании объявляет имя [namespace.udecl] p1 (http://eel.is/c++draft/namespace.udecl#1):

Объявление-использование вводит имя в декларативную область в
который появляется объявление об использовании.

используя декларирование:
   using typenameвыбирать вложенное имя спецификатор неквалифицированный-ID ;

Имя члена, указанное в объявлении об использовании, объявляется в
декларативный регион, в котором появляется объявление об использовании. [ Заметка:
Только указанное имя объявляется так; указание имени перечисления
в объявлении об использовании не объявляет свои перечислители в
декларативный регион использования-декларации. — конец примечания] Если
использование-объявления именует конструктор ([class.qual]), он неявно
объявляет набор конструкторов в классе, в котором
появляется объявление об использовании ([class.inhctor]); иначе имя
указанный в объявлении использования является синонимом для набора
объявления в другом пространстве имен или классе.

Итак, у нас есть 4 объявления имени i.

Что из этого делает поиск безусловного имени i в sizeof(i) находить?

Это только найти using M::i; а также using N::i; которые находятся в одном и том же пространстве имен (глобальном пространстве имен), поэтому программа хорошо сформирована?

Или это только найти struct i {}; а также static int i = 1; которые находятся в разных пространствах имен, так что программа плохо сформирована?

Или у нас есть другая альтернатива?

2

Решение

Bogdan уже должен ответить, но чтобы понять, почему ваша интуиция неверна, вы процитировали:

Если поиск имени находит объявление для имени в два разных пространства имен, и объявления не объявляют одну и ту же сущность и не объявляют функции, использование имени неправильно сформировано.

Но в примере мы имеем:

namespace M {
struct i {};           // declares M::i, entity class type
}
namespace N {
static int i = 1;      // declares N::i, entity variable
}
using M::i;                // declares ::i, synonym of M::i
using N::i;                // declares ::i, synonym of N::i
// hides (*) the other ::i
int main() {
sizeof (i);
}

Разработать (*)у нас есть две декларации i в глобальном пространстве имен ::, Из [basic.scope.hiding]:

Имя класса (9.1) или имя перечисления (7.2) можно скрыть по имени переменной, члена данных,
Функция или перечислитель объявлены в той же области видимости. Если имя класса или перечисления и переменная, данные
член, функция или перечислитель объявляются в той же области (в любом порядке) с тем же именем,
имя класса или перечисления скрыто там, где имя переменной, члена данных, функции или перечислителя
видимый.

Так с двумя iв той же области, класс скрыт (независимо заказа из с помощью деклараций!), а также sizeof(i) относится к ::i это синоним N::i, И то и другое iс были в так же пространство имен (::), поэтому ваша цитата не применяется. Это отличается от вашего предыдущий вопрос, где ты был используя-директивы вместо:

using namespace M;
using namespace N;

Там i будет найдено в двух разных пространствах имен, ссылаясь на две разные нефункциональные сущности. Отсюда и ошибка. Здесь Clang не прав, а GCC прав.

1

Другие решения

N4527 [7.3.3p13]:

Так как используя декларирование это декларация, ограничения на
объявления с одинаковыми именами в одном и том же декларативном регионе (3.3)
также относится к с помощью деклараций. [ Пример:

namespace A {
int x;
}

namespace B {
int i;
struct g { };
struct x { };
void f(int);
void f(double);
void g(char);    // OK: hides struct g
}

void func() {
int i;
using B::i;      // error: i declared twice
void f(char);
using B::f;      // OK: each f is a function
f(3.5);          // calls B::f(double)
using B::g;
g(’a’);          // calls B::g(char)
struct g g1;     // g1 has class type B::g
using B::x;
using A::x;      // OK: hides struct B::x
x = 99;          // assigns to A::x
struct x x1;     // x1 has class type B::x
}

— конец примера ]

Обратите внимание с помощью деклараций для двух разных xs — это тот же случай, что и ваш пример.


Ваша первая цитата относится к используя-директивы, не с помощью деклараций.

Неквалифицированный поиск имени для i в sizeof(i) находит is в глобальном пространстве имен. Поскольку они являются объявлениями в одной и той же области видимости, согласно [3.3.10p2] (цитата ниже), переменная i прячет struct,

Имя класса (9.1) или имя перечисления (7.2) может быть скрыто по имени
переменной, члена данных, функции или перечислителя, объявленного в
та же сфера Если имя класса или перечисления и переменная, данные
член, функция или перечислитель объявляются в одной и той же области видимости (в любом
order) с тем же именем, имя класса или перечисления скрыто
где имя переменной, члена данных, функции или перечислителя
видимый.

Таким образом, код правильно сформирован, и Clang ошибочно его отвергает.

MSVC (12 и 14) принимает пример.


В основном, подумайте об имени, введенном используя декларирование как просто другое имя для некоторой сущности, которая также названа где-то еще (место, обозначенное вложенное имя спецификатор из Квалифицированный-идентификатор в используя декларирование). Это отличается от того, что с помощью директивы делает; Я склонен думать о используя-директивы как «твики поиска имени».

1

По вопросам рекламы [email protected]