4

When I compile the following code with the latest Visual Studio, it success to compile.

class C;

class T
{
public:
    template<typename A>
    void f();

private:
    C* c;
};

int main()
{
    T t;
    t.f<int>();
}

template<typename A>
void T::f()
{
    this->c->g();
}

class C
{
public:
    void g() {}
};

But when I remove this-> from this->c->g(), compilation fails with C2027: use of undefined type 'C'.

When I make the method f non-template, it fails to compile no matter this-> presents or not, so I think it's related to template compiling/instantiating, but I can't really figure out. I've read this answer, but isn't c and g unambiguous in T::f()?

So, the question is: What's the role of this-> here?


Compiler Differences:

+-----------------------+---------------------+----------------------+--------------+
|                       | Template, w/ this-> | Template, w/o this-> | Non-Template |
+-----------------------+---------------------+----------------------+--------------+
| Visual Studio 16.3.10 | Success             | Fail                 | Fail         |
| x64 msvc v19.24       | Success             | Success              | Fail         |
| x86-64 gcc 9.2        | Success w/ warning  | Success w/ warning   | Fail         |
| x86-64 clang 9.0.0    | Fail                | Fail                 | Fail         |
+-----------------------+---------------------+----------------------+--------------+

x64 msvc v19.24, x86-64 gcc 9.2 and x86-64 clang 9.0.0 are tested with Compiler Explorer.

yeshjho
  • 350
  • 2
  • 11
  • 2
    I think this _should_ fail to compile, so really only clang is correct. When `T.f()` is first instantiated, it doesn't yet have the definition of `C`, and therefore should fail. – ChrisMM Feb 13 '20 at 20:14
  • @ChrisMM Well, it should fail at the template definition, not just at instantiation. GCC's also "right" in that it knows it should be failing. – HTNW Feb 13 '20 at 20:17
  • @HTNW, why would the template definition not be okay? If the definition `C` is moved before the definition of `template void T::f()`, then it would be correct. Or, is this what you mean? – ChrisMM Feb 13 '20 at 20:23
  • @ChrisMM `T` is not dependent on `A`, therefore `this` is not dependent on `A`, therefore the access to `c` is not dependent on `A`, therefore the access of `g` in `C` is not dependent on `A`, therefore it should be checked at the definition of the template and not at its instantiations, therefore the template itself is ill-formed. It would be well-formed if the definition of `C` is visible at the template definition. – HTNW Feb 13 '20 at 20:31
  • @HTNW, _Non-dependent names used in a template definition are found using the usual name lookup and **bound at the point they are used.**_ In OP's example, `this->c->g()` isn't used in the template definition, only in the `T::f()` definition. It is still ill-formed as is, since `c` (with or without `this`) does not have a full definition of `C` yet. So yes, not the instantiation of `T.f()` like I said in my first comment, that was incorrect of me. It is when `T::f()` is beging defined. – ChrisMM Feb 13 '20 at 20:37
  • @ChrisMM there is some distinctions in compilers about how template compilation is processed. In MSVC only a few checks are performed at template definitions, most only performed at template instantiations. Some other compilers test too much unnecessarily on template definitions. It is a mess overall. This shouldn't compile at the template instantiation. Hell, the definition shouldn't be visible at all at the call. So it probably compiles at all due to some bug. – ALX23z Feb 13 '20 at 20:49
  • @ALX23z, MS actually says on their website they improperly parse templates like this, and that they violate the clause I quoted above. (`[temp.nondep]`) – ChrisMM Feb 13 '20 at 20:58
  • @ALX23z msvc claim to have fixed all that stuff, https://devblogs.microsoft.com/cppblog/two-phase-name-lookup-support-comes-to-msvc/ – M.M Feb 13 '20 at 21:01

1 Answers1

1

The program is ill-formed NDR due to C++17 [temp.res]/8.3:

The program is ill-formed, no diagnostic required, if:

  • [...]
  • a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter,

The hypothetical instantiation is ill-formed because c->g is used when c has pointer to incomplete type, and that is not affected by the template parameter A.

So it is a quality of implementation issue whether an error is raised.

M.M
  • 138,810
  • 21
  • 208
  • 365