c++language-lawyername-lookupusing-declarationname-hiding

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


Is this ill-formed or well-formed according to the c++ standard?

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

Clang rejects it and GCC accepts it.

According to [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.

How should we interpret this? Remember that each using-declaration are declaring a name by [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.

So we have 4 declarations of the name i.

Which of these does unqualified name lookup of i in sizeof(i) find?

Does it only find using M::i; and using N::i; which are both in the same namespace (the global namespace) so the program is well-formed?

Or does it only find struct i {}; and static int i = 1; which are in different namespaces so the program is ill-formed?

Or do we have some other alternative?


Solution

  • bogdan already has to the answer, but to build on why your intuition is incorrect, you cited:

    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.

    But in the example, we have:

    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); 
    }
    

    To elaborate on (*), we have two declaration of i in the global namespace ::. From [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.

    So with the two is in the same scope, the class is hidden (irrespective of the ordering of the using-declarations!), and sizeof(i) refers to the ::i that is the synonym of N::i. Both is were in the same namespace (::), which is why your quote doesn't apply. This differs from your earlier question, where you had using-directives instead:

    using namespace M;
    using namespace N;
    

    There i would be found in two different namespaces, referring to two different non-function entities. Hence, the error. Here, Clang is wrong and GCC is correct.