error: expected ‘;‘ before ‘<‘ token

created at 12-28-2021 views: 5

error

Recently I was playing with C++ template meta, but once the compiler reported a confusing error:

  • Version: g++ (MinGW.org GCC Build-2) 9.2.0
  • Standard: c++17
test.cpp:23:36: error: expected ';' before '<' token
   23 | using result = typename List::Push<T>::result;
      |                                   ^
      |                                   ;

The reason for the story is this. I am writing a TypeList with a meta function Push<typename T>, which is used to add a type to the end of the TypeList.

//A simplified TypeList definition
template <typename... Ts>
struct TypeList {
   //The definition of Type Node does not matter here, so ignore it.

   template <typename T>
   struct Push {
     using result = TypeList<Ts..., T>;
   };
};

using list1 = TypeList<int, double, void>;
using list2 = typename list1::Push<bool>::result; //The type of list2 is TypeList<int, double, void, bool>

int main(){}

So far, everything compiles well. But if I want to define a meta function outside of TypeList, it accepts a type List instantiated by TypeList and a new type T to be added, and then in this meta function What about calling Push<T>?

template <typename List, typename T>
struct Invoker {
   using result = typename List::Push<T>::result; //error!
};

//using list3 = typename Invoker<list2, char>::result; //This sentence will report an error even if you don't write the above.

So there is the above error message, it seems to instruct us to delete everything after List::Push, like: using result = typename List::Push;, in fact, it can indeed pass Compile, but once the sentence in list3 is commented out, the code still has to report an error. Because there is no Push nested class in the TypeList we defined, but only the nested class template Push<typename T>. But write one A simple Push class instead of a class template will not allow us to add new types. This problem has troubled me for a long time, and I haven’t found any results on the Internet. I can only take some very detours to solve it.

solution

In the end, it is in cppreference from which I found the correct wording should be like this:

//Add the template keyword between the scope resolver:: and the template name Push.
using result = typename List::template Push<T>::result; //OK

This can solve the problem perfectly. Here, the template keyword is used to disambiguate, similar to the role of typename. This is related to a concept pending name in C++. Here, typename List::Push< The result of T>::result depends on the template parameter List, which is indeterminate before instantiation, that is to say, the compiler did not know what type of List was at the time, let alone the ones in List What member, what inner class. And the Push that depends on the formal parameter List, for things like List::Push, the compiler first parses Push into List A static variable name, function name, or internal class name, rather than a template of it-although a pair of angle brackets is behind the Push. So the compiler sees a so-called static variable (or other Thing) followed by a pair of angle brackets, it is directly wrong. If you add a template before Push, it will disambiguate and make the compiler treat Push as a template, which is correct Compile.

According to the explanation of cppreference, when performing a qualified name search for expressions that depend on template parameters (that is, there is a scope resolver), if there is no disambiguation keyword, the compiler will first treat it as a value pending Expressions, such as: using result = List::Push<T>::result, the expression result on the right side of the equal sign will be treated as a value instead of a type, so an error is reported, and errors like this are also found online, and the compiler can often To be clear, the typename keyword is missing, so we add it: using result = typename List::Push<T>::result, so that the type is named on the right side of the equal sign, but it still reports an error, because the compiler puts the Push inList::Push::resultTake it as a static variable/static function/internal class name instead of a template name, and these things cannot be followed by angle brackets, so you need to add template to indicate that Push is a template name, so that it can be successfully compiled. The error of missing template disambiguation is very cryptic, and it is difficult for people who don't understand what is wrong.

created at:12-28-2021
edited at: 12-28-2021: