0

I am having difficulty understanding why the following piece of code does not compile (MSVC 2022, C++20 mode):

#include <string>
template <typename T>
void d1(std::basic_string<T>::iterator x)
{
}

MSVC compiler gives very cryptic collection of 4 error messages and two warnings for the same location:

error C2182: 'd1': this use of 'void' is not valid
warning C4346: 'std::basic_string<_Elem,std::char_traits<_Elem>,std::allocator<_Ty>>::iterator': dependent name is not a type message : prefix with 'typename' to indicate a type
error C2146: syntax error: missing ')' before identifier 'x'
error C2143: syntax error: missing ';' before '{'
error C2447: '{': missing function header (old-style formal list?)

The only thing here that at least gives some sort of a hint is warning C4345, so I tried this and it fixed the problem:

#include <string>
template <typename T>
void d2(typename std::basic_string<T>::iterator x)
{
}

But I don't want to use this solution mindlessly and now I would like to learn what underlying C++ mechanism requires the use of the typename keyword in order to compile the code successfully.

In my attempt to understand this, I tried to construct other code examples to see at which point the error occurs, and this is what I got:

//iterator of std::string class is passed as an argument, compiles fine
void a1(std::string::iterator x)
{
}

//iterator of std::basic_string<char> class is passed as an argument, compiles fine
void b1(std::basic_string<char>::iterator x)
{
}

//std::basic_string<> is passed as an argument into template function, compiles fine
template <typename T>
void c1(std::basic_string<T> x)
{
}

//iterator of std::basic_string<> is passed as an argument into template function, fails to compile
template <typename T>
void d1(std::basic_string<T>::iterator x)
{
}

//iterator of std::basic_string<> is passed as an argument into template function (prefixed by typename keyword), compiles fine
template <typename T>
void d2(typename std::basic_string<T>::iterator x)
{
}

Basically everything works fine without the use of typename keyword until I try to do both of the following:

  1. Use a template
  2. Pass an iterator

Separately these two things are acceptable for the compiler, but a combination of them makes the compiler choke.

So it is a mystery to me why functions a1, b1, c1 compile fine without the use a a typename keyword, and only functions d1/d2 require the typename keyword as an argument prefix.

Regus Pregus
  • 560
  • 3
  • 12
  • 1
    TL;DR: It's required when a member type name is dependent on template parameter. – Miles Budnek Jul 15 '23 at 01:53
  • @MilesBudnek: Thank you for TLDR (and the link to a duplicate question, reading it now), however, isn't the argument of function c1 in my example also dependent on template parameter (and yet it compiles fine)? – Regus Pregus Jul 15 '23 at 02:31
  • 1
    It is, but it is not a _member type_ of a type dependent on a template parameter. `typename` is needed when you have things like `Foo::some_type` since `some_type` could be a type or an object and the compiler doesn't know which until it knows what `T` is, so it assumes it's an object unless you tell it otherwise using the `typename` keyword. – Miles Budnek Jul 15 '23 at 02:37
  • @MilesBudnek: Thank you so much, now it all makes sense to me. – Regus Pregus Jul 15 '23 at 03:50

0 Answers0