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:
- Use a template
- 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.