3

I was reading about the usage of typename in C++ template programming (e.g. this Q/A). To me, it seems that when using a dependent nested type name, we should use typename for avoiding parsing ambiguity. I also checked this on Scot Meyers book effective C++, item #42.

But what is strange for me is that the same example in the book, works without the typename. Here is the code:

template<class C>
void Print2nd(const C & cont)
{
   if (cont.size() >= 2)
   {
      C::const_iterator * iter1 = new C::const_iterator(cont.begin());  // why typename is NOT needed?
      C::const_iterator   iter2 = cont.begin();                         // why typename is NOT needed?
      (*iter1)++;
      iter2++;
      int value1 = **iter1;
      int value2 = *iter2;

      std::cout << "The value of 2nd with pointer is: " << value1 << std::endl;
      std::cout << "The value of 2nd without pointer is: " << value2 << std::endl;
   }
}


int main()
{
   std::vector<int> vect = {1,2,3,4,5,6};
   Print2nd(vect);
   return 0;
}

I am using VS2015. So, the Q is that why typename is not needed in this context? Is there any upgrade in recent C++ compilers to avoid using typename in such a context? Or I am doing a mistake in the code?

Update 1: Thanks to @FrançoisAndrieux comment, it seems that the same thing is happening in VS2008 and VS2010, as reported in this Q/A.

TonySalimi
  • 8,257
  • 4
  • 33
  • 62

2 Answers2

5

In typename is not needed there. In some contexts, the need for typename was removed, because syntactically anything there must be a type.

In particular:

A qualified name that appears in type-id, where the smallest enclosing type-id is:

  • the type in a new expression that does not parenthesize its type;

Quoted source isn't directly from the standard, but pretty reliable.

Prior to typename was needed there; it would be parsed as a value, and new value is not valid syntax. In typename is optional in that context.

Now, has no features in it; what you are seeing there is MSVC's failure to properly implement //, not a extension.

Community
  • 1
  • 1
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    So it was a bug, now a feature? ;) – NathanOliver Sep 25 '19 at 15:26
  • @NathanOliver The bug was more akin to "compiler ignores syntax errors in function that is never called, because this specific compiler does compiling at link-time". The feature is akin to "certain things in this function are not syntax errors". They both make certain code not break the compile, but the fact that the bug covered the feature is sort of accidental. – Yakk - Adam Nevraumont Sep 25 '19 at 15:30
4

typename is needed; the example program is ill-formed. If the compiler does not diagnose the issue, then it doesn't conform to the standard. A correct version is:

typename C::const_iterator * iter1 = new typename C::const_iterator(cont.begin());
                                      // ^^^^^^^^ this one only required until C++20
typename C::const_iterator   iter2 = cont.begin();

Standard quote (draft for C++17):

[temp.res]

A name used in a template declaration or definition and that is dependent on atemplate-parameteris assumednot to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.

C::const_iterator depends on the template-parameter C, so it should not be assumed to be a type name unless typename is used. I think the statement should be interpreted as a multiplication operation, but the right hand operand is undeclared identifier and thus ill-formed.

C++20 introduces rule that allows removing the typename from the new-expression (latest draft):

[temp.res]

A qualified name is said to be in a type-id-only context if it appears in a type-id, new-type-id, or defining-type-id and the smallest enclosing type-id, new-type-id, or defining-type-id is a new-type-id, defining-type-id, trailing-return-type, default argument of a type-parameter of a template, or type-id of a static_­cast, const_­cast, reinterpret_­cast, or dynamic_­cast.


You might want to create a type alias for readability:

using const_iterator = typename C::const_iterator;

Or you could just use auto:

auto it = cont.begin();

P.S. It hardly ever makes sense to dynamically allocate an iterator.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326