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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With