2

Does the C++ standard say anything about the exact point in time, when the code for the virtual methods of class templates is generated?

Consider the following example:

class Interface
{
  public:
    virtual void f() = 0;
};

template <unsigned int V>
class A : public Interface
{
  public:
    virtual void f()
    {
    }
};

Interface* instantiate()
{
  // class template instantiation with argument V=0
  return new A<0>();
}

// specialization of f() for template argument V=0
template <> void A<0>::f()
{
  cout << "Output from A<0>::f()" << endl;
};

int main() 
{
  Interface* i = instantiate();
  i->f();
  return 0;
}

The class template A declares a virtual method f(). In our example the function instantiate() implicitly instantiates the class template A, before any explicitly specialization of A<0>::f() has been done. In the above example, the specialization is done after implicit instantiation of class template A has happened. Now, at least my ARM-Compiler and g++ pick the specialized version of A<0>::f(), i. e. the main() program prints “Output from A<0>::f()” to the screen.

Can I always be sure, that it is sufficient to define the specialization of a virtual method of a class template after this class template has been implicitly instantiated? I would feel better, if the observed behaviour was backed by the C++ standard. I did not find any clear statement about this topic. The closest part would be 14.7.3/6, which is somewhat unspecific when it comes to virtual methods:

If a template, a member template or the member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instan- tiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit specialization that is declared but not defined

Pete
  • 23
  • 2
  • 1
    watch out - no virtual destructor – BЈовић Sep 19 '13 at 15:42
  • 1
    C++11 14.7.1p10 says that "It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated.", but that's not the entire story, I am afraid. – Angew is no longer proud of SO Sep 19 '13 at 15:55
  • The question is does `new A<0>()` cause an implicit instantiation of the class template member `A<0>::f`. I suspect the answer is yes, and as such your program is ill-formed - no diagnostic required. – Andrew Tomazos Sep 19 '13 at 15:56
  • Related: http://stackoverflow.com/q/18174368/420683 – dyp Sep 19 '13 at 16:27
  • I think the Standard is pretty clear about this: [temp.expl.spec]/7 "When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation." (No, I'm kidding. The Standard is not very clear about this. The quote however, is authentic.) – dyp Sep 19 '13 at 16:39

2 Answers2

2

We're pretty sure it's UB.

In practice:

new A<0>()

will generate a call to the constructor, and the compiler requires a definition of it to be available. If you try to specialize A<0>::A() after this call gcc will error:

error: specialization of ‘A<V>::A() [with V = 0]’ after instantiation

The constructor will have the code to set up the polymorphic header of the class, which will contain a pointer to a vtable. In that vtable will be the entry for Interface::f, but it doesn't even have declared at this point the symbol that will eventually fill out that slot, your explicit specialization A<0>::f - so it comes down to a quality-of-implementation issue- does the compiler design the vtable at the same time it completes the class type - and if so is it capable of fixing up a newly declared member of that vtable later in the TU.

Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
  • As I just commented on Sebastian Redl's answer: If the virtual function is implicitly instantiated, then its point of instantiation is immediately after the point of instantiation of the enclosing class template specialization as per [temp.point]/4. Although we do have an explicit specialization here that prevents implicit instantiation, this is not known at the line `new A<0>()`. As there is `i->f();`, the virtual function is required to be instantiated I think, therefore it should be instantiated before the explicit specialization is visible. – dyp Sep 19 '13 at 16:49
  • @DyP: I agree with your analysis, it is ill-formed NDR. – Andrew Tomazos Sep 19 '13 at 16:55
  • There's very few "undefined behaviour" in the template section, what are you exactly referring to? I could think of a violation of the ODR because we have the implicit instantiation and the explicit specialization; [temp.expl.spec]/6 only says something like "shall not" and "no diagnostic is required". (Is is *undefined behaviour* because there's no explicit requirement?) – dyp Sep 19 '13 at 17:01
  • @DyP: Its confusing I know: "ill-formed no diagnostic required" and "undefined behaviour" actually mean exactly the same thing. The standard imposes no requirements on an implementation in those circumstances. Some have speculated that ill-formed NDR is a hint that the compiler might be able to catch it during translation, but that theory doesn't seem to bare itself out in the text. Officially there is no difference between the two phrasings. – Andrew Tomazos Sep 19 '13 at 17:03
  • Yeah... I just thought the same and edited the comment some seconds ago ;) – dyp Sep 19 '13 at 17:04
  • I have one more questions though: What if we change the program like this? What if we declare the specialization before the first instantiation, like this: template class A : public Interface { public: virtual void f() { } }; // declaration of explicit specialization template <> void A<0>::f(); // ... (like in the original example This should be standard conforming, because the compiler was told before implicit instantiation, that there will be a specialization for V=0. What do you think? – Pete Sep 19 '13 at 18:05
  • @Pete: Yes, I think a forward declaration of the explicit virtual member specialization would be sufficient to solve the practical problem, whether it solves the standard-conformance issue is another matter. – Andrew Tomazos Sep 19 '13 at 18:12
  • @user1131467 I agree, but I think it's even standard-compliant. "that specialization shall be declared before the first **use** of that specialization that would **cause** an implicit instantiation to take place" So it's not even important to be before the point of instantiation, it just has to be before anything that would otherwise *cause* an implicit instantiation. We still don't know if there's something that causes impl. instantiation, but the first possibility here is `new A<0>()`, and with the declaration immediately after the class, it should be fine. – dyp Sep 19 '13 at 18:18
  • @DyP: I concur, the key is that it says "shall be declared" not "shall be defined". – Andrew Tomazos Sep 19 '13 at 18:23
0

The standard is pretty unclear on this. The relevant section on implicit instantiation is 14.7.1p2:

[...] the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist;

"requires the definition to exist" is unfortunately a completely undefined term, but I think a good argument can be made that "odr-used" is at least a subset thereof. For "odr-used", the big wall of text in 3.2p2 says:

A virtual member function is odr-used if it is not pure.

In other words, virtual members are required by virtue (no pun intended) of their mere existence.

So I think an argument can be made that the compiler is at least allowed to try to instantiate all virtual functions the moment it instantiates the containing class. I don't know of any compiler that does that (AFAIK, they all delay instantiation until the end of the translation unit unless forced not to), but I think your code is strictly non-conforming.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • You can't delay instantiation until the end of the translation unit, you have to do it while parsing in order to disambiguate names between types and non-types. For example `T<42>::x * y`. To determine whether this declares a pointer to the type x, or a call to `operator*` you need to instantiate `T<42>`. – Andrew Tomazos Sep 19 '13 at 16:04
  • Function instantiation can be delayed. Type instantiation can't. I was talking about the virtual functions. – Sebastian Redl Sep 19 '13 at 16:06
  • ...but actually, completing the type of a template class instantiation and having definitions of its members are not the same thing. I would say the intention of the standard is that you should put all your declarations and definitions of template entities, including specializations, ahead of any use of them - and if you don't its UB - this gives compiler authors flexibility to assume they have already seen any definitions they may want during a single pass of the TU. – Andrew Tomazos Sep 19 '13 at 16:23
  • As Angew already quoted, there's also [temp.inst]/10 "An implementation shall not implicitly instantiate a function template, a member template, a non-virtual member function, a member class, or a static data member of a class template that does not require instantiation. It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated." – dyp Sep 19 '13 at 16:30
  • [temp.point]/4 "If a virtual function is implicitly instantiated, its point of instantiation is immediately following the point of instantiation of its enclosing class template specialization." – dyp Sep 19 '13 at 16:43
  • @DyP: Yes, see my answer - I explain the implementation issue with virtual functions. It has to do with setting up the vtable for the class, which could reasonably be done at the same time the class is completed. – Andrew Tomazos Sep 19 '13 at 16:46
  • @user1131467 That could be the rationale behind it, yes. – dyp Sep 19 '13 at 16:50