10

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
  • 7,718
  • 2
  • 40
  • 52
Martin
  • 918
  • 5
  • 22
  • 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

11

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
  • 918
  • 5
  • 22
ecatmur
  • 145,219
  • 25
  • 281
  • 356
  • 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
  • 3
    @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 ` – 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
  • 63,700
  • 7
  • 105
  • 170
  • 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