4
struct A
{
    virtual ~A() { f(); }

    virtual void f() {}
};

I've edited my question to be more specific..

In this code sample, MAY the call f() use virtual dispatch, or is it guaranteed equivalent to A::f()?

Could you provide relevant section from C++ standard? Thanks.

Neil Kirk
  • 21,327
  • 9
  • 53
  • 91
  • Almost certain that the call is made virtually and that you shouldn't do it because it might call a method of a derived class whose constructor hasn't been called yet. – zneak Aug 27 '13 at 04:04
  • Besides, it would be very easy to test, wouldn't it? – zneak Aug 27 '13 at 04:04
  • 2
    @zneak Not really, that will just tell me what my compiler did, not what it is permitted to do. – Neil Kirk Aug 27 '13 at 04:04
  • 1
    @zneak That isn't possible from within a constructor in C++. It will call the 'most-constructed' override. – user207421 Aug 27 '13 at 04:05
  • @NeilKirk I generally trust my compilers to do standard things. – zneak Aug 27 '13 at 04:06
  • @zneak Why are you so pedantic? This is contentious issue in another question, about whether the compiler can call virtually but may optimize to non-virtual if it wants to, or it must always be non-virtual. – Neil Kirk Aug 27 '13 at 04:07
  • I'm being quite the opposite of pedantic. I'm suggesting you [trust very easily found empirical evidence](http://ideone.com/YKGzW6) (which shows I was wrong, too) instead of [referring to the standard](http://open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf). – zneak Aug 27 '13 at 04:12
  • @zneak http://stackoverflow.com/questions/18456450/is-it-safe-to-call-a-pure-virtual-function-in-an-abstract-constructor-destructor This is the question that caused this one. It may shed some light on why I am asking. – Neil Kirk Aug 27 '13 at 04:13
  • 1
    NeilKirk: The method in that question is **pure** `virtual`. It's not just `virtual`. That's why calling it is undefined (nothing else would make sense). @EJP is correct. – user541686 Aug 27 '13 at 04:14
  • @Mehrdad Why doesn't it make sense? A pure virtual can have a body. If it has a body, why cannot it be called? – Neil Kirk Aug 27 '13 at 04:18
  • @NeilKirk: ... because I had no idea that pure virtual functions can have a body lol. Oops... – user541686 Aug 27 '13 at 04:19
  • related: http://stackoverflow.com/q/496440/; very related: http://stackoverflow.com/q/11377629/ – zneak Aug 27 '13 at 04:20
  • 1
    @zneak My question is about whether the function call is resolved virtually, statically or it is up to the compiler. Not which is the final function selected. – Neil Kirk Aug 27 '13 at 04:23
  • @zneak You can't always trust the compilers to do standard things. The standard does not, cannot, and should not define *all* behaviors. What's left is undefined, implementation-defined, or unspecified behavior, and whatever your compiler does for them is inherently non-standard. – jamesdlin Aug 27 '13 at 04:25
  • 1
    @jamesdin I don't see the relevance of this digression, unless you can either show that this *is* undefined behaviour or produce a compiler that doesn't comply with the standard. – user207421 Aug 27 '13 at 04:28
  • @NeilKirk The "final function selected" is the function that is called by virtual dispatch. Are you interested in knowing which function will be called, or in if a virtual table lookup will occur? – zneak Aug 27 '13 at 04:29
  • @zneak If a virtual table lookup may occur with proof from the standard. – Neil Kirk Aug 27 '13 at 04:30
  • @NeilKirk /q/113377629 says §12.7.4 says "Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class’s non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor’s or destructor’s class and not one overriding it in a more-derived class.". – zneak Aug 27 '13 at 04:31
  • @zneak In a constructor or destructor, is `f()` guaranteed to have the same behavior as `A::f()`? – Neil Kirk Aug 27 '13 at 04:35
  • @NeilKirk: The standard says nothing about virtual tables or any other implementation details of virtual dispatch; it just defines the required behaviour. In this case, it just specifies which override must be called. The program must behave *as if* virtual dispatch occurred with a dynamic type of `A`. The compiler is likely to optimise this to non-virtual dispatch since the dynamic type is known within the destructor. The observed behaviour is the same either way, unless the function is pure virtual, in which case the behaviour is undefined. – Mike Seymour Aug 27 '13 at 04:36
  • @NeilKirk No, because A::f() is guaranteed to be non-virtual, regardless of where it is. – user207421 Aug 27 '13 at 04:38
  • @EJP I'm simply saying that it's perfectly fine to ask what the standard behavior for something is without being criticized that "it's easy to test". – jamesdlin Aug 27 '13 at 04:41
  • @jamesdlin I agree with that, who wouldn't? but it's not what you actually said. I *do* trust compilers to do standard things, and I've only aware of one, unfortunately rather prominent, C++ compiler that doesn't implement the standard properly ... in quite other respects. – user207421 Aug 27 '13 at 05:07
  • @EJP Oops, sorry. I meant to write, "You can't trust compilers to do *only* standard things". – jamesdlin Aug 27 '13 at 05:47

4 Answers4

7

Within a constructor or destructor, the sub-class object has either not yet been constructed, or has already been destroyed. As a result, virtual dispatch does not lead to the derived-class version being used, and instead the base-class version is called.

From the standard, [class.cdtor]/4:

Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class’s non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor’s or destructor’s class and not the one overriding it in a more-derived class. If the virtual function call uses an explicit class member access (5.2.5) and the object expression refers to the complete object of x or one of that object’s base class subobjects but not x or one of its base class subobjects, the behavior is undefined.

An example of this is given:

struct V {
   virtual void f();
   virtual void g();
};
struct A : virtual V {
   virtual void f();
};
struct B : virtual V {
   virtual void g();
   B(V*, A*);
};
struct D : A, B {
   virtual void f();
   virtual void g();
   D() : B((A*)this, this) { }
};
B::B(V* v, A* a) {
    f(); // calls V::f, not A::f
    g(); // calls B::g, not D::g
    v->g(); // v is base of B, the call is well-defined, calls B::g
    a->f(); // undefined behavior, a’s type not a base of B
}

Also note that this can be unsafe if the function that is called is pure virtual, from [class.abstract]/6:

Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.

Mankarse
  • 39,818
  • 11
  • 97
  • 141
  • 1
    Thanks. As this is contentious issue elsewhere, can you provide evidence from the standard? – Neil Kirk Aug 27 '13 at 04:08
  • There's nothing in my copy of the standard that says 'virtual despatch does not occur', although this answer is correct as to which override gets called. The C++ Object Model book makes it clear that this is accomplished by adjusting the vtable pointer during construction, not by eliminating virtual despatch. – user207421 Aug 27 '13 at 04:13
  • So is my call to `f()` equivalent to `A::f()`? If f is pure virtual, it would seem Not. – Neil Kirk Aug 27 '13 at 04:20
  • @Mankarse There's still nothing there that says anything about virtual despatch not occurring. – user207421 Aug 27 '13 at 04:21
  • Calling a pure virtual method from an constructor is undefined behavior: "Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined." §10.4.6 – zneak Aug 27 '13 at 04:24
  • @zneak: That only applies to pure virtual functions. That's why I noted that exception in my answer. – Mankarse Aug 27 '13 at 04:26
  • @zneak The effect of making a `virtual call`.. now you see the crux of the matter! – Neil Kirk Aug 27 '13 at 04:32
  • 1
    I've realised that the wording that I used originally was slightly misleading. It is better to think of it as the type of the object changing during construction. Virtual dispatch will still happen (in situations where the static and dynamic types can differ), but never to a function in a more-derived class than the one currently being constructed. – Mankarse Aug 27 '13 at 04:35
  • @NeilKirk: Optimizers take huge advantage of every clause in the standard (well many clauses) that mark some code as *undefined behavior*. Even if the standard explicitly mandated dynamic dispatch in huge bold letters, the compiler is free to transform dynamic dispatch into static dispatch. If the call is safe (the function is not pure virtual) the final overrider is known and the *as-if* rule applies. If the function is pure virtual, your program has undefined behavior, and the compiler is not constraint by the standard as of what behavior to produce, and calling `A::f` is better than demons – David Rodríguez - dribeas Aug 27 '13 at 04:41
  • @DavidRodríguez-dribeas It just seems to me like it is disallowed for no technical reason.. – Neil Kirk Aug 27 '13 at 04:44
  • @NeilKirk: Well, I have seen that fail in some compilers (admittedly a few years back). While the wording in the standard could separate the *directly or indirectly* cases into two, that would only complicate the language (mostly for humans) for no good reason. Note that even the *indirect* call could be required to work in the standard without having a great impact on performance, by just requiring more virtual tables, as is the case with virtual inheritance. The compiler could set the vptr to a vtable for "A under construction or destruction" and then reset at the end of the constructor... – David Rodríguez - dribeas Aug 27 '13 at 04:48
  • [...] and that would actually provide some advantage. But in the case of a direct call from the constructor/destructor, the programmer is free to call the function directly, without using dynamic dispatch. You do know who the final overrider is, just qualify with the type of the object being constructed/destructed. – David Rodríguez - dribeas Aug 27 '13 at 04:51
  • @DavidRodríguez-dribeas That's exactly how it does work. See the C++ Object Model book and my other comments in this thread. – user207421 Aug 27 '13 at 05:01
  • @EJP: I am familiar with how it works, I was considering the options to remove undefined behavior from the calls to a pure virtual function that has a definition. On a closer look, the problem is not one of different virtual tables (the single vtable per type suffices, it just needs to store the pointer to the definition of the pure virtual function), yet that might complicate the linker process since it would need to manage symbols that are either used (if there is a definition) or not (if there is no definition), creating a new category and potentially other weird behavior. – David Rodríguez - dribeas Aug 27 '13 at 13:14
2

The standard does not require that the call is performed dynamically or statically. Conceptually it is dynamic, and all of the quotes regarding the call of a virtual function while an object is being constructed or destructed contain the text directly or indirectly. That is important, as it entails that whether the call is direct to the function or not the behavior should be the same. Now consider:

struct A {
   A() { f(); }
   void f() { g(); }
   virtual void g() {};
};

And assume that code is not visible and all the usual caveats so that the functions are not inlined. The definition of A::f() which might be in a different translation unit, does not know whether it is called from the constructor or destructor or neither of them. It does not know if the complete object is of type A or Z for any derived type Z, so it must use dynamic dispatch.

Now, the as-if rule means that the compiler has some leeway to optimize, and it can decide that within the body of the constructor/destructor (or any function inlined into it) the final overrider is known and it can thus avoid dynamic dispatch and call the known final overrider directly. This works even for pure virtual functions, since in that case, the behavior is undefined, and thus there are no guarantees of behavior --so any transformation by the compiler will be valid in that case.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
0

§12.7.4:

Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class’s non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor’s or destructor’s class and not one overriding it in a more-derived class.

§10.4.6:

Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.

Virtual dispatch occurs and the function called is the last override in the class chain until the one being constructed. If the function was never implemented, the behavior is undefined.

// well-defined behavior, calls A::f()
struct A
{
    A() { f(); }
    virtual void f();
};

struct B
{
    virtual void f();
};

// well-defined behavior, calls B::f()
struct C : public B
{
    C() { f(); }
};

// well-defined behavior, calls D::f()
struct D : public B
{
    D() { f(); }
    virtual void f(); 
};

// calling a pure virtual method from a constructor: undefined behavior
// (even if D::f() is implemented at a later point)
struct D
{
    D() { f(); }
    virtual void f() = 0;
};

// specifying f() as pure virtual even if it has an implementation in B,
// then calling it from the constructor: undefined behavior
struct E : public B
{
    E() { f(); }
    virtual void f() = 0;
};
zneak
  • 134,922
  • 42
  • 253
  • 328
-3

Yes. Any unqualified reference to a member from within its class is entirely equivalent to

this->member

or

this->member(...)

as appropriate, and therefore despatched virtually if it's a virtual function. The standard doesn't make any exceptions about calls from constructors or destructors. It does make an exception about which function is called, but not about how that is accomplished.

EDIT

The actual VFT mechanism used to implement this exception is described in [1]. As Lippman points out, simply erasing virtual despath is not an acceptable technique, as any indirect virtual function call called by the called virtual function is also subject to the same exception ('directly or indirectly' clause).

[1] Lippman, Stanley B., *Inside the C++ Object Model,* Addison Wesley 1996, pp179ff.
user207421
  • 305,947
  • 44
  • 307
  • 483
  • 1
    I don't understand your answer. – Neil Kirk Aug 27 '13 at 04:06
  • While true, this doesn't have anything to do with virtual functions. – jamesdlin Aug 27 '13 at 04:22
  • Yes it does. It says that all unqualified ways of calling a virtual function are the same and therefore implies that all are despatched virtually. – user207421 Aug 27 '13 at 04:23
  • `this->f()` and `f()` are the same in my code sample. I don't understand how this is new information? – Neil Kirk Aug 27 '13 at 04:27
  • I don't understand your point. There is no 'this->f()' in your sample. – user207421 Aug 27 '13 at 04:29
  • @EJP No, because what you said applies to non-virtual functions as well. And your implication is wrong, because C++ explicitly suppresses virtual dispatch in constructors and destructors to prevent virtual functions from being invoked on objects that haven't been fully constructed. – jamesdlin Aug 27 '13 at 04:30
  • @jamesdlin The relevant section of the standard is cited in another answer and it says no such thing. It says *which* function is called, but not *how.* There is no excuse for repeating this misinformation here. – user207421 Aug 27 '13 at 04:33
  • @jamesdlin do you have standard quote for that??? As that is the answer to my question!! – Neil Kirk Aug 27 '13 at 04:33
  • @EJP The citation in the other answer explains it fine. You can call a virtual function from a constructor or destructor, but it's *not* the version from a derived class ("... not the one overriding it in a more-derived class..."). Therefore, it's not doing normal virtual dispatch. – jamesdlin Aug 27 '13 at 04:35
  • @jamesdin The standard doesn't say that at all. The required effect can indeed be accomplished by virtual dispatch. The object's vtable pointer is modified during construction. See the C++ Object Model book. It can also be accomplished by non-virtual despatch, but the standard doesn't mandate it. – user207421 Aug 27 '13 at 04:37
  • @EJP Okay, I see what you're saying. Sure, it could still go through the motions (and have the cost) of a virtual function call. However, that's a pedantic point that doesn't really answer what Neil Kirk is asking (which is whether an override in a derived class may be called at all). And furthermore, your answer still draws the wrong conclusion (where you claim that it "therefore [is] despatched [sic] virtually", which I acknowledge *might* be true but isn't necessarily so). – jamesdlin Aug 27 '13 at 04:39
  • @jamesdlin First, there is nothing and has never been anything in the question about derived classes, so you are misrepresenting the question here. Second, I'm not aware of any compiler that doesn't use the vtable-adjusting technique, but it's admittedly a long time since I looked. Third, as simply erasing virtual dispatch doesn't satisfy the 'directly or indirectly' wording of the standard, my conclusion is reasonable. – user207421 Aug 27 '13 at 05:18