12

The following code compiles correctly in g++ and clang:

template<typename T>
struct foo
{
    class iterator;
    using bar = foo::iterator;
};

int main() {}

however MSVC 2013 gives the following errors:

foo.cpp(9): error C2061: syntax error : identifier 'iterator'
          foo.cpp(10) : see reference to class template instantiation 'foo<T>' being compiled
foo.cpp(9): error C2238: unexpected token(s) preceding ';'

If I change that line to:

using bar = typename foo::iterator;

then all three compilers compile it successfully. Is the original version correct? (i.e. is this a MSVC bug, or a gcc/clang extension)

M.M
  • 138,810
  • 21
  • 208
  • 365
  • 1
    Allowed, yes. Required, no. `foo::iterator` names a member of the current instantiation. – T.C. May 13 '15 at 04:17

2 Answers2

7

[temp.res]/p3:

When a qualified-id is intended to refer to a type that is not a member of the current instantiation (14.6.2.1) and its nested-name-specifier refers to a dependent type, it shall be prefixed by the keyword typename, forming a typename-specifier.

[temp.dep.type]/p1:

A name refers to the current instantiation if it is

  • in the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, the injected-class-name (Clause 9) of the class template or nested class,
  • [...]

[temp.dep.type]/p4:

A name is a member of the current instantiation if it is

  • An unqualified name that, when looked up, refers to at least one member of a class that is the current instantiation or a non-dependent base class thereof. [ Note: This can only occur when looking up a name in a scope enclosed by the definition of a class template. —end note ]
  • A qualified-id in which the nested-name-specifier refers to the current instantiation and that, when looked up, refers to at least one member of a class that is the current instantiation or a non-dependent base class thereof. [ Note: if no such member is found, and the current instantiation has any dependent base classes, then the qualified-id is a member of an unknown specialization; see below. —end note ]
  • [...]

foo is the current instantiation. foo::iterator is a qualified-id in which the nested-name-specifier (foo::) refers to the current instantiation, and when looked up, "refers to at least one member of a class that is the current instantiation or a non-dependent base class thereof"; it therefore is a member of the current instantiation. Therefore, [temp.res]/p3 does not apply, and no typename is required. You are still allowed to add one - or just use iterator unqualified directly.

T.C.
  • 133,968
  • 17
  • 288
  • 421
2

From the standard:

14.6.2.1 Dependent types [temp.dep.type]

1 A name refers to the current instantiation if it is

__ in the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, the injected-class-name (Clause 9) of the class template or nested class,

The name foo refers to the current instantiation, that is obvious.

Since iterator is declared as a nested class in the definition of the template, iterator refers to the name in the current instantiation of foo. foo::iterator is the same as iterator.

using bar = foo::iterator;

as well as

using bar = iterator;

should work.

It seems to me that you have run into a MSVC defect.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270