The interpretation of [basic.scope.hiding] p2 when searching for an unqualified name includes the use of directives

There are two types of name hiding in C ++:

1) Hiding the normal name: [basic.scope.hiding] p1 ( http://eel.is/c++draft/basic.scope.hiding#1 ):

A name can be hidden by explicitly declaring the same name in a nested declarative scope or derived class ([class.member.lookup]).

2) A special type of name is hidden in [basic.scope.hiding] p2 ( http://eel.is/c++draft/basic.scope.hiding#2 ):

A class name ([class.name]) or an enumeration name ([dcl.enum]) can be hidden by a variable name, data member, function, or enumerator declared in the same scope. If a class or enumeration name and a variable, data item, function, or enumerator are declared in the same scope (in any order) with the same name, class, or enumeration, the name is hidden wherever the variable, data item, function, or counter name is visible.

I am interested to know how name hiding interacts with using-directives when doing unqualified name validation.

For the first type of name, which obscures behavior, it is clear. This is because [basic.scope.hiding] p1 has been reformulated in terms of the rules in the [basic.lookup.unqual] section ( http://eel.is/c++draft/basic.lookup.unqual )

The same was not done to hide the second type name. So the next question is:

*) How does this hidden type of the second type interact with unqualified name lookups that involve using-directives?

Elsewhere in the standard I find [namespace.udir] p2 ( http://eel.is/c++draft/namespace.udir#2 ) and I think this is the key to answering this question

The using directive indicates that names in the nominated namespace can be used in the scope in which the use directive appears after the use directive. When searching for an unqualified name ([basic.lookup.unqual]), the names appear as if they were declared in the nearest enclosing namespace that contains both using-directive and the nominated namespace. [Note: In this context, "contains" means "contains, directly or indirectly". - end note]

Applying the as if part of this rule to [basic.scope.hiding] p1 matches the rules in the [basic.lookup.unqual] section. This app is also compatible with [basic.scope.hiding] p4 ( http://eel.is/c++draft/basic.scope.hiding#4 ). This looks promising.

In this regard, I think that we can answer the question *), just as if using the [namespace.udir] p2 part on [basic.scope.hiding] p2. This app is also compatible with [basic.scope.hiding] p4. I think this is also the most natural and least difficult interpretation of the C ++ standard.

However, the problem is that Clang and GCC do not do the same interpretation as I do. For example:

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

      

According to my interpretation, this program should be well formed and i

should be treated like an integer variable. Both Clang and GCC disagree with this, ambiguity in name lookups.

In the Clan's case, this more complex interpretation leads to the following error:

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

      

Gives no errors but changes

using namespace P;
using namespace Q;

      

in

using namespace Q;
using namespace P;

      

and we get an ambiguous name error. GCC is at least consistent here.

Have I interpreted the C ++ standard correctly?

+3


source to share


2 answers


I consider the key phrases here:

The name can be hidden by an explicit declaration of the same name in a nested declarative scope, or by a derived class (10.2).

A class name (9.1) or an enumeration name (7.2) can be hidden by a variable name, data member, function, or enumerator declared in the same scope .

In this example:

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

      

Both i

are declared in different, non-nested areas, so there is no hide. A name lookup finds them as if they were announced in ::

, but that's not what the hideout rule stipulates.



Otherwise, we have [basic.lookup]:

A name lookup must find an unambiguous declaration for the name (see 10.2). A name lookup can associate more than one declaration with a name if it considers the name to be a function name;

There is ::

no unambiguous declaration, so this code is poorly formed and the error is correct. The same is true for the other example, so the fact that there is some order of use of the declaration for which clang compilation is a bug.

Although not normative, there is an example in [namespace.udir] that makes this interpretation clear:

[Note: in particular, the name of a variable, function, or enumerator does not hide the name of a declared class or enumeration in another namespace. For example,

namespace A {
    class X { };
    extern "C" int g();
    extern "C++" int h();
}

namespace B {
    void X(int);
    extern "C" int g();
    extern "C++" int h(int);
}

using namespace A;
using namespace B;
void f() {
   X(1); // error: name X found in two namespaces
   g();  // OK: name g refers to the same entity
   h();  // OK: overload resolution selects A::h
}

      

-end note]

+3


source


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

      

It is ill-formed. ยง7.3.4 / 6:

If a 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.

The same goes for your second example. Note that the name hiding rule that applies, for example,



struct A {} A;

      

... doesn't apply in your case as the two i

are being declared in different scopes. Besides,

When searching for an unqualified name ([basic.lookup.unqual]), names are displayed as if they were declared in the nearest enclosing namespace that contains both the use-directive and the nominated namespace.

It doesn't matter, as any ambiguity that a name lookup produces, as in your examples with i

, is considered after the lookup - here, for example. the aforementioned ยง7.3.4 / 6.

+2


source







All Articles