3

Is the following (in)correct c++ code and why?

class MyC;
class MyB {
public:
     template <class MyT> static void Gimme() { MyT(); }
     MyB() { Gimme<MyC>(); }
} B_;

class MyC  {
public: MyC() { }
};

g++ 4.7.2 does not complain.

bogdan
  • 9,229
  • 2
  • 33
  • 48
blazee
  • 168
  • 1
  • 8

2 Answers2

4

I'd guess that the following portion of C++14 standard is at work here

14.6.4.1 Point of instantiation

8 A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. [...]

(Emphasis mine.)

Note that this portion of document was changed significantly in C++14 compared to C++11. Also see DR#993, which seems to imply that simply postponing such instantiations to the end of translation unit is a valid implementation technique.

I would go as far as cautiously state that your code is ill-formed under C++11 instantiation rules, even though GCC accepts it in -std=c++11 mode.

Community
  • 1
  • 1
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 2
    I'm not following your explanation - 14.6.4.1/1 appears to say that `Gimme` should be instantiated as soon as the definition of `MyB` is closed. I don't see how an *additional* point of instantiation at the end of the unit is supposed to allow the compiler to ignore the first such point. – M.M Feb 10 '16 at 01:25
  • @M.M: On the one hand, that is something I don't immediately understand myself. I don't see a clear requirement of whether *all* instantiation points should be valid or just *one* is sufficient. On the other hand, again, DR#993 seems to imply that an implementation can simply ignore all "intermediate" instantiation points and just instantiate it at the very end of the TU. It refers to this as "permissible implementation technique". Go figure... – AnT stands with Russia Feb 10 '16 at 01:33
  • 1
    You need to read three sentences forward into that paragraph: *If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required.* Ignoring all other points of instantiation except the end of the TU is a valid implementation technique because of the *no diagnostic required* part, but that doesn't make the code well-formed. cc @M.M – bogdan Feb 10 '16 at 09:07
  • What about the aschepler's comment (http://stackoverflow.com/questions/7210286/incomplete-class-usage-in-template)? Do not points of instantiation come into play only after the type in this case was already completed? – blazee Feb 10 '16 at 13:21
  • @bogdan: I have read it. I just don't any definitive wording that would explain what "different meanings" means in this case. At all points the semantics of the code is unambiguous: the expression performs a cast using a functional notation `T()`. Whether having the type `T` incomplete at some of these points qualifies as giving a "different meaning" to the corresponding specialization... well, again, I don't see a definitive answer to that question anywhere in the document. – AnT stands with Russia Feb 10 '16 at 15:25
  • @bogdan: Moreover, if the answer at the link given by blazee is correct, then having the type initially incomplete and later complete does not constitute a change in the "meaning" of the specialization. It was constructing a temporary before, it still constructs a temporary after. The meaning hasn't changed. – AnT stands with Russia Feb 10 '16 at 15:30
  • The notion of point of instantiation is defined independently of the phases of translation, and so is the notion of incomplete type. I don't think that referring to the phases of translation is relevant in this case. As far as I can tell, the author of the linked answer says the same thing in the comments. [3.9p5 and p6] tell us that `MyC` is an incomplete type in the body of `MyB`'s constructor, but complete at the end of the TU. [5.2.3p1] tells us that using an incomplete type for an explicit type conversion expression is ill-formed. cc @blazee – bogdan Feb 10 '16 at 16:28
  • So, the instantiation `Gimme` is ill-formed within `MyB`'s constructor, but well-formed at the end of the TU. I'd say that constitutes "different meaning". Otherwise, we'd have to accept that the following two different programs are equivalent: both define the non-template function `static void Gimme() { MyC(); }`, but one places it in the same place as the template in the example, and the other one at the end of the TU. The first program doesn't compile, the second one does. Do they have "the same meaning"? – bogdan Feb 10 '16 at 16:29
-1

Yes, this is legal C++ code. You declare MyC as forward declaration, and the definition is in the same scope later. And because the C++ compiler uses at least 2 passes to parse the source files, this is correct and will generate correct machine code. Your "incomplete" type MyC is not incomplete.

ul90
  • 762
  • 3
  • 8
  • "uses at least 2 passes to parse the source files" is not how it works in C++. C++ has *two-phase name lookup* for dependent names in template definitions. – AnT stands with Russia Feb 10 '16 at 00:30
  • @AnT: yes, you're right. I just simplified it a little bit ;-) But 2 passes are not sooo wrong: first the preprocessor, then the c++ parsing (i know, this is not relevant for this question)... – ul90 Feb 10 '16 at 00:33