Why is no template keyword needed if an unrelated

2019-04-07 10:28发布

问题:

This question is related to my previous question Compiler error when trying to call template method from private instance, which was pointed out to be related to this question: Where and why do I have to put the "template" and "typename" keywords?

So I read this and I get the idea that the C++ language definition is ambiguous so it cannot always be parsed correctly. In my case, the answer was that I need a.template f<1>() in B::test() to help the parser understand that it is dealing with a template. Fine.

But, after reading all this, why the heck is the parser suddenly able to do without the template keyword if I happen to have a completely unrelated global template function that happens to have the same name? This compiles without problems and behaves as expected:

#include <iostream>

template <int i>
void f() {std::cout << "f()\n";}

template <int N>
struct A {
    template <int i>
    void f() {std::cout << "A::f()\n";}
};

template <int N>
struct B {
    A<N> a;

    B(A<N>& a) : a(a) {}

    void test() {
        f<1>();
        a.f<1>();  // compiles without 'template' keyword!
    }
};

int main() {
    A<2> a;
    a.f<1>();   // works fine
    B<2> b(a);
    b.test();
}

I found that the global function must:

  • be called f
  • be a template function
  • be defined before B

Otherwise, it can be pretty much anything. So

template <typename T, unsigned k>
void *f(double x, const char *s) {return NULL;}

works just as well to help the parser out that a.f<1>() in B::test() is in fact to be parsed as a.template f<1>().

What is the compiler thinking? Like: "Well, the guy already has a global template function called f<>(), so when I'm parsing this completely unrelated expression a.f<1>() inside B::test(), I'm going to assume that it's also a template function?" What is this?

What did I miss when reading Where and why do I have to put the "template" and "typename" keywords??

Update

The code above compiles for me with all of:

  • i686-apple-darwin11-llvm-g++-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.9.00)
  • Apple clang version 3.1 (tags/Apple/clang-318.0.58) (based on LLVM 3.1svn)
  • g++-4.8 (GCC) 4.8.2

I also tested with the compiler flags -pedantic -Wall -Wextra and with -std=c++11 for g++-4.8. It worked in all cases.

Update 2

This also works without template keyword:

// ...
template <int N, template <int> class A>
struct B {
    A<N> a;

    B(A<N>& a) : a(a) {}

    void test() {
        f<1>();
        a.f<1>();  // compiles without 'template' keyword!
    }
};

int main() {
    A<2> a;
    a.f<1>();   // works fine
    B<2, A> b(a);
    b.test();
}

回答1:

I think g++ is correct and the code should compile without .template. According to the standard (at least up to my understanding),

3.4.5/1 (page 55 of N3936)

In a class member access expression (5.2.5), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template.