使用 using 声明时,非限定名称查找如何工作?

How does unqualified name lookup work when using using-declarations?

根据 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):

If name lookup finds a declaration for a name in two different namespaces, and the declarations do not declare the same entity and do not declare functions, the use of the name is ill-formed.

我们应该如何解读?请记住,每个 using 声明都通过 [namespace.udecl]p1 (http://eel.is/c++draft/namespace.udecl#1):

声明一个名称

A using-declaration introduces a name into the declarative region in which the using-declaration appears.

using-declaration:
   using typenameopt nested-name-specifier unqualified-id ;

The member name specified in a using-declaration is declared in the declarative region in which the using-declaration appears. [ Note: Only the specified name is so declared; specifying an enumeration name in a using-declaration does not declare its enumerators in the using-declaration's declarative region. — end note ] If a using-declaration names a constructor ([class.qual]), it implicitly declares a set of constructors in the class in which the using-declaration appears ([class.inhctor]); otherwise the name specified in a using-declaration is a synonym for a set of declarations in another namespace or class.

所以我们有 4 个名称 i 的声明。

sizeof(i)i 的非限定名称查找找到了哪些?

是否只找到同一个命名空间(全局命名空间)中的 using M::i;using N::i; 所以程序是良构的?

还是它只找到位于不同命名空间的 struct i {};static int i = 1;,所以程序格式错误?

或者我们有其他选择吗?

N4527 [7.3.3p13]:

Since a using-declaration is a declaration, the restrictions on declarations of the same name in the same declarative region (3.3) also apply to using-declarations. [ Example:

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
}

—end example ]

请注意两个不同 xusing-declarations - 这与您的示例相同。


你的第一句话是指 using-directives,而不是 using-declarations

sizeof(i)i 的非限定名称查找在全局命名空间中找到 i。由于它们是同一范围内的声明,根据 [3.3.10p2](下面引用),变量 i 隐藏了 struct.

A class name (9.1) or enumeration name (7.2) can be hidden by the name of a variable, data member, function, or enumerator declared in the same scope. If a class or enumeration name and a variable, data member, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the variable, data member, function, or enumerator name is visible.

因此,代码格式正确,Clang 拒绝它是错误的。

MSVC(12 和 14)接受示例。


基本上,将 using-declaration 引入的名称视为某个实体的另一个名称,该名称也在其他地方命名([=33 指定的位置) =]nested-name-specifier of the qualified-id in the using-declaration)。这与 using-directive 的作用不同;我倾向于将 using-directives 视为 "name lookup tweaks".

已经有了答案,但为了说明为什么您的直觉不正确,您引用了:

If name lookup finds a declaration for a name in two different namespaces, and the declarations do not declare the same entity and do not declare functions, the use of the name is ill-formed.

但在示例中,我们有:

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]:

A class name (9.1) or enumeration name (7.2) can be hidden by the name of a variable, data member, function, or enumerator declared in the same scope. If a class or enumeration name and a variable, data member, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the variable, data member, function, or enumerator name is visible.

因此,对于同一范围内的两个 i,class 被隐藏( 的顺序无关 using-declarations!),而sizeof(i)指的是::i,它是N::i的同义词。两个 i 都在 相同的 命名空间 (::) 中,这就是为什么您的引述不适用的原因。这与您的 不同,您 using-directives 而不是:

using namespace M;
using namespace N;

在两个不同的命名空间中可以找到 i,指的是两个不同的非功能实体。因此,错误。在这里,Clang 是错误的,而 GCC 是正确的。