Eos Pengwern
Eos Pengwern

Reputation: 1547

Template code that compiles in gcc 8.2 but not MSVC 19

This example script compiles cleanly in gcc 8.2, but in Visual Studio 2019 it returns error C3200: 'bar<int>': invalid template argument for template parameter 'bar', expected a class template at the line where the 'new' occurs:

template<typename T, template<typename> class bar>
class foo;

template<typename T>
class bar
{
    friend class foo<T, bar>;
};

template<typename T, template<typename> class bar>
class foo
{
};

class baz : bar<int>
{
public:
    void mash()
    {
        auto whoosh = new foo<int, bar>();
    }
};

int main(int argc, char **argv)
{
    baz A;
    A.mash();

    return 0;
}

Reading around, I think the problem may be that the second template argument in that line is now a well-defined type rather than a template, but even if that's true I don't know what to do about it. Replacing 'bar' with 'decltype(bar)' didn't get me anywhere.

I would welcome any suggestions.

https://godbolt.org/z/rrGdvf763

Upvotes: 3

Views: 117

Answers (1)

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122133

I admit, I don't know who is right or wrong here (see below). The confusion seems to be from the fact that inside the class template baz inheriting from bar<int> the identifier bar is interpreted as bar<int>. Strangely bar itself uses bar to refer to the template not to bar<T>. And the compilers do not agree. Anyhow, all compilers you included in the conformance view accept the code if you change it to:

class baz : bar<int>
{
public:
    void mash()
    {
        auto whoosh = new foo<int, ::bar>();
    }
};

Live Demo

As Jarod42 pointed out:

From injected-class-name#In_class_template: "In the following cases, the injected-class-name is treated as a template-name of the class template itself: - it is used as a template argument that corresponds to a template template parameter". so msvc's bug.

And, thanks to aschepler for the link, the official wording in the standard can be found in [temp.local] in paragraph 1.

So what I called "strange" above is to be expected. For example:

 template <typename T>
 struct moo {
      moo some_method();
      //^ refers to moo<T>
      foo<int,moo> some_other_method();
      //      ^ refers to moo
 };

And what you encountered is a case where msvc does not correctly implement that exception.

Upvotes: 3

Related Questions