C++ Function Template Voodoo

I came across this in Ceph source code

class ConfigProxy {
  ...

  template<typename T>
  const T get_val(const std::string_view key) const {
    std::lock_guard l{lock};
    return config.template get_val<T>(values, key);
  }

  ...
};

Whoa wait! What? Did you ever seen a literal space ` ` in member function name?

    return config.template get_val<T>(values, key);

That extra template seems to be around for quite some time, I found this StackOverflow question from almost 12 years ago!

template<class X> struct A {
  template<int I> void f() {}
};

template<class T> void g()
{
  A<T> a;
  a.f<3>(); // Compilation fails here
}

int main(int argc, char *argv[])
{
  g<int>();
}

At a first glance, it seems pretty obvious that we want to call a.f() with template argument specialized as 3.

But it fails with

$ g++ -std=c++20 test.cc
test.cc: In function ‘void g()’:
test.cc:17:16: error: expected primary-expression before ‘)’ token
   17 |         a.f<3>();
      |                ^
test.cc: In instantiation of ‘void g() [with T = int]’:
test.cc:26:8:   required from here
test.cc:17:12: error: invalid operands of types ‘<unresolved overloaded function type>’ and ‘int’ to binary ‘operator<’
   17 |         a.f<3>();
      |         ~~~^~

Why? Well, take a close look at the error log, you can see that the compiler say otherwise: It trying to compare a.f with 3, and sees a.f < 3 as a translation unit! It’s basically that nested-template-needs-to-be-declared-as-vector<vector<X> >-with-a-space tokenizer voodoo all over again! In 2021!

Therefore, as an adhoc patch, you need to explicitly give the keyword template before your templated member function, so the tokenizer will recognize that what’s after . or -> is a function, and can never be a member variable, and treat <3> as a whole. The following example with extra spaces may demonstrate this better

  // test.cc:19:14: error: invalid operands of types ‘<unresolved overloaded
  // function type>’ and ‘int’ to binary ‘operator<’
  //  19 |         (a.f < 3) > ();
  //     |         ~~~~~^~~~
  (a.f < 3) > ();       // equivalent to a.f<3>(), at least to the tokenizer
  operator<(a.f, 3) > (/*an empty parenthesized expression*/);

  a. template f <3> (); // a.template f<3>(), will compile

The more you know!