Both gcc 5.0 and clang 3.6 require the typename
keyword in the following example:
template<int n>
struct I
{
typedef int Type;
};
template<typename T>
struct A
{
int m;
void f()
{
typedef typename I<sizeof m>::Type Type; // typename required
}
};
This is covered by the following wording in the C++11 standard:
[temp.dep.type]/8
A type is dependent if it is
- a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent
So I<sizeof m>
is dependent if sizeof m
is value-dependent.
[temp.dep.expr]/4
Expressions of the following forms are never type-dependent (because the type of the expression cannot be dependent):
sizeof unary-expression
[temp.dep.constexpr]/2
Expressions of the following form are value-dependent if the unary-expression or expression is typedependent or the type-id is dependent:
sizeof unary-expression
So sizeof m
is only dependent if m
is dependent.
[expr.prim.general]/8
Within the definition of a non-static member function, an identifier that names a non-static member is transformed to a class member access expression
So m
is the member in a class member access expression.
[temp.dep.type]/4
A name is a member of the current instantiation if it is
- An id-expression denoting the member in a class member access expression (5.2.5) for which the type of the object expression is the current instantiation, and the id-expression, when looked up (3.4.5), refers to at least one member of the current instantiation or a non-dependent base class thereof.
So it seems that m
is a member of the current instantiation.
[temp.dep.type]/5
A name is a member of an unknown specialization if it is
An id-expression denoting the member in a class member access expression (5.2.5) in which either
the type of the object expression is the current instantiation, the current instantiation has at least one dependent base class, and name lookup of the id-expression does not find a member of the current instantiation or a non-dependent base class thereof; or
the type of the object expression is dependent and is not the current instantiation.
So m
is NOT a member of an unknown specialization - it would be found by name lookup to be a member of the current instantiation.
[temp.dep.expr]/3
An id-expression is type-dependent if it contains
- an identifier associated by name lookup with one or more declarations declared with a dependent type,
- a nested-name-specifier or a qualified-id that names a member of an unknown specialization
Since m
is of type int
and is not a member of an unknown specialization, neither of these bullets would make the id-expression m
dependent.
[temp.dep.expr]/5
A class member access expression (5.2.5) is type-dependent if the expression refers to a member of the current instantiation and the type of the referenced member is dependent, or the class member access expression refers to a member of an unknown specialization.
When m
is transformed to a class member access expression, it is still not dependent because it does not refer to a member of an unknown specialization.
Should m
be treated as dependent? On a related note, should this->m
be treated as dependent? What about std::declval<A>().m
?
EDIT
And finally, should &A::m
be dependent?
sizeof m
is not dependent, but the compilers seem to think that is is dependent. Is there a flaw in my proof?" And the answer is: "No, I can't see any flaw." The same arguments should apply tothis->m
andstd::declval<A>().m
, since in any case the object expression refers to the current instantiation, andm
has no dependent type. – dypstd::declval<A>().m
is complicated by the fact that the lefthand side of the class member access is type-dependent, making it impossible (?) to determine whether it refers to the current instantiation – willjm
is translated to justthis->m
? So if that is problematic, so is plainm
. – Alan Stokes