11

I have the following code:

template <typename TC>
class C
{
    struct S
    {
        template <typename TS>
        void fun() const
        {}
    };

    void f(const S& s)
    {
        s.fun<int>();
    }
};

// Dummy main function
int main()
{
    return 0;
}

When building this with both gcc 9.2 and clang (9.0), I'm getting a compilation error due to the template keyword being required for invoking fun. Clang shows:

error: use 'template' keyword to treat 'fun' as a dependent template name
        s.fun<int>();
          ^
          template 

I don't understand why the compiler thinks fun is a dependent name in the context of f, since f is not a template itself. If I change C to be a regular class instead of a template, the error goes away; however, I don't see why there should be an error in the first place since neither S nor f depend on TC.

Oddly enough, MSVC 19.22 compiles this just fine.


note

Before voting to close as dupe of Where and why do I have to put the "template" and "typename" keywords? please consider this is a special case where even if S is indeed a dependent name, in the context of f it would not be dependent if not for the fact that they are members of the current instantiation.

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Martin
  • 940
  • 6
  • 26
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/207227/discussion-on-question-by-martin-c-why-is-the-template-keyword-required-he). – Bhargav Rao Feb 05 '20 at 00:41

2 Answers2

13

Consider:

template<typename T>
struct C
{
    struct S
    {
        int a = 99;
    };

    void f(S s, int i)
    {
        s.a<0>(i);
    }
};

template<>
struct C<long>::S
{
    template<int>
    void a(int)
    {}
};

int main()
{
    C<int>{}.f({}, 0); // #1
    C<long>{}.f({}, 0); // #2
}

s.a<0>(i) is parsed as an expression containing of two comparison operations < and >, and this is fine for #1 but fails for #2.

If this is changed to s.template a<0>(i) then #2 is OK and #1 fails. Thus the template keyword is never redundant here.

MSVC is capable of interpreting the expression s.a<0>(i) both ways within the same program. But this is not correct according to the Standard; each expression should have only one parse for the compiler to deal with.

Martin
  • 940
  • 6
  • 26
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • I still don't fully understand this. Your example shows that in this case you either use a specialization of `C` or the other, but you can't ever instantiate both. The `template` keyword here is still unneeded IMHO, because which `S` gets picked depends on which `C` you instantiate. Without the `template` keyword you'd be able to instantiate both, and f's behavior would differ for each instance. – Martin Feb 04 '20 at 21:34
  • 4
    @Martin the point is that each token should have only one syntactic role in the source file. For example, it's not OK for the token `<` to be a comparison operator in one template instantiation and an opening angle bracket in another instantiation. This is to ensure that compilers can parse templates to an AST (with placeholders for template types). – ecatmur Feb 05 '20 at 11:13
  • That makes sense. Thanks! – Martin Feb 05 '20 at 14:14
7

fun may or may not be a template function (or may not exist at all) depending on the template parameter of class C.

That's because you can specialize S (without specializing C):

template <> struct C<int>::S {};

Because the compiler wants to know if fun is a template or not when first looking at class C (prior to substituting the template parameter), template is required.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • The follow-up to this, then, is whether these new definitions of `S` can ever be accessed by `f`. If they can't, having this restriction doesn't make sense because `f` won't be able to see them anyways. – Martin Feb 04 '20 at 18:21
  • @Martin With both [GCC, Clang](https://gcc.godbolt.org/z/fPXydt), and MSVC `f` does find it. – HolyBlackCat Feb 04 '20 at 18:31
  • @bolov Yeah, you can specialize [lots of different things](https://en.cppreference.com/w/cpp/language/template_specialization). – HolyBlackCat Feb 04 '20 at 18:36