Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should a name found in a dependent base class hide a namespace name at the point of instantiation?

With the following code, clang 3.0 gives error: lookup of 'N' in member access expression is ambiguous, while clang 3.4 and gcc 4.8 both accept the code without error.

struct B
{
    struct M
    {
        void f()
        {
        }
    };
};

namespace N
{
    struct M
    {
        void f()
        {
        }
    };
}

template<typename>
struct A : N::M, B::M
{
    typedef B N;
};

struct D : A<int>
{
    A<int> m;
    void f()
    {
        m.N::M::f(); // found class-name 'A<int>::N' (unambiguous)
    }
};

template<typename T>
struct C : A<T>
{
    A<T> m;
    void f()
    {
        m.N::M::f(); // found namespace-name 'N' (ambiguous?)
    }
};

template struct C<int>;

After consulting the standard it is not clear to me which behaviour is correct with regards to the expression in C<T>::f().

Because N is looked up both in the scope of the class of the object expression n (which is dependent) and in the context of the entire postfix expression (i.e. the scope of the function C<T>::f()), it is necessary to delay the lookup until the point of instantiation.

At the point of instantiation, the lookup will be ambiguous if it finds both the namespace N and the typedef A<T>::N. The declaration of N is only visible if it is not hidden by the declaration of A<T>::N.

The question is whether or not the namespace N should be considered to be hidden by the typedef A<T>::N when looking up N "in the context of the entire postfix-expression" and "at the point of definition of the template".

Quoted from C++ Working Draft standard N3242=11-0012 (Feb 2011):

3.4.5 Class member access [basic.lookup.classref]

If the id-expression in a class member access is a qualified-id of the form

class-name-or-namespace-name::...

the class-name-or-namespace-name following the . or -> operator is looked up both in the context of the entire postfix-expression and in the scope of the class of the object expression. If the name is found only in the scope of the class of the object expression, the name shall refer to a class-name. If the name is found only in the context of the entire postfix-expression, the name shall refer to a class-name or namespace-name. If the name is found in both contexts, the class-name-or-namespace-name shall refer to the same entity.

14.6.4 Dependent name resolution [temp.dep.res]

In resolving dependent names, names from the following sources are considered:

Declarations that are visible at the point of definition of the template.

— Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context.

like image 857
willj Avatar asked Aug 13 '13 10:08

willj


1 Answers

This is something that was changed in C++11. The text you quote is from C++03; before C++11, this was ambiguous, because both lookups were used, and it was an error if they found different names. In C++11, the text is:

If the id-expression in a class member access is a qualified-id of the form class-name-or-namespace-name::... the class-name-or-namespace-name following the . or -> operator is first looked up in the class of the object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire postfix-expression. [ Note: See 3.4.3, which describes the lookup of a name before ::, which will only find a type or namespace name. —end note ]

Basically, this privileges the lookup in class scope, and doesn't do the other one if the name is found.

As to why this only affected the template in the older versions of the standard: I think (it's hard to be sure about anything here) that it is because in the case of the non-template, the lookup in the context of the entire postfix-expression also finds the typedef in the base class, so both lookups resolve to the same entity. In the case of the template, the lookup in the context of the entire postfix-expression doesn't take into account the dependent base class, and only finds the namespace N. After instantiation of C, however, the lookup in the scope of the class finds the typedef. Since the two lookups find different entities, the name binding is ambiguous.

like image 150
James Kanze Avatar answered Nov 15 '22 11:11

James Kanze