12

My question is not about calling a virtual member function from a base class constructor, but whether the pointer to a virtual member function is valid in the base class constructor.

Given the following

class A
{
    void (A::*m_pMember)();

public:
    A() :
        m_pMember(&A::vmember)
    {
    }

    virtual void vmember()
    {
        printf("In A::vmember()\n");
    }

    void test()
    {
        (this->*m_pMember)();
    }
};

class B : public A
{
public:
    virtual void vmember()
    {
        printf("In B::vmember()\n");
    }
};

int main()
{
    B b;
    b.test();

    return 0;
}

Will this produce "In B::vmember()" for all compliant c++ compilers?

anorm
  • 2,255
  • 1
  • 19
  • 38
  • I would guess not. Inside A's constructor, `*this` is an object of class A yet, and the vtable is A's vtable. Moreover, I would be surprised if `&A::vmember` resulted in a pointer to `B::vmember`. But then again, I am not a C++ guru. – Péter Török Jun 22 '10 at 11:55
  • @Péter Török: Yes, but that's the point of the vtable, isn't it. The location stays the same but subclasses fills their function pointers into the vtable of the base class. Hence, the function pointer is correct but calling it within the constructor is not. Note that the function pointer is invoked after the constructor is finished – anorm Jun 22 '10 at 11:59
  • 1
    Hmmm... _might_ be. Still, my rough understanding is that `&A::vmember` results not in a pointer to the vtable, but a pointer to a concrete member function. – Péter Török Jun 22 '10 at 12:01
  • @Peter: it compiles under g++ 4.4.1 and prints "In B::vmember()\n" so the virtual mechanism is working in this case. But I can't locate a relevant piece of info in the standard... – Francesco Jun 22 '10 at 12:04
  • @Francesco: That is my understanding as well. I know it compiles and generates the desired result using g++, but is this in some way documented in the standard? – anorm Jun 22 '10 at 12:08
  • @Dr. Sbaitso: same output also with vc++ 2008. – Francesco Jun 22 '10 at 12:11
  • 1
    @Francesco: You can test with every possible compiler on Earth, if this isn't clearly specified in the standard, you'll still be unable to conclude that "it works". – ereOn Jun 22 '10 at 12:23
  • @ereOn: spot on, and one cannot even conclude that it will continue to work with newer releases of a compiler. That is the exact reason for which I reported the version # of g++ and of vc++. – Francesco Jun 22 '10 at 12:47
  • For bonus point: I can't find anywhere in msdn what would elicit such a behavior. It's completely baffling me. `b.A::vmember()` produces a different result from `(b.*&A::vmember)()`... that's certainly suprising :/ – Matthieu M. Jun 22 '10 at 17:34
  • 1
    @ereOn: It is solidly and clearly defined in the standard. – AnT stands with Russia Jun 22 '10 at 18:25
  • @AndreyT: I'm glad it is :) That wasn't my point tought ;) – ereOn Jun 22 '10 at 19:03

5 Answers5

3

"Valid" is a specific term when applied to pointers. Data pointers are valid when they point to an object or NULL; function pointers are valid when they point to a function or NULL, and pointers to members are valid when the point to a member or NULL.

However, from your question about actual output, I can infer that you wanted to ask something else. Let's look at your vmember function - or should I say functions? Obviously there are two function bodies. You could have made only the derived one virtual, so that too confirms that there are really two vmember functions, who both happen to be virtual.

Now, the question becomes whether when taking the address of a member function already chooses the actual function. Your implementations show that they don't, and that this only happens when the pointer is actually dereferenced.

The reason it must work this way is trivial. Taking the address of a member function does not involve an actual object, something that would be needed to resolve the virtual call. Let me show you:

namespace {
  void (A::*test)() = &A::vmember;
  A a;
  B b;
  (a.*test)();
  (b.*test)();
}

When we initialize test, there is no object of type A or B at all, yet is it possible to take the address of &A::vmember. That same member pointer can then be used with two different objects. What could this produce but "In A::vmember()\n" and "In B::vmember()\n" ?

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 2
    I actually find it baffling that `b.A::vmember()` and `(b.*&A::vmember)()` do not produce the same effect... – Matthieu M. Jun 22 '10 at 17:31
  • 1
    Should be `(a.*test)()`. Same for `b`. – AnT stands with Russia Jun 22 '10 at 18:15
  • The baffling difference is a result of a syntactic similarity between two expressions that semantically aren't that similar. In particular, the first doesn't use a pointer to a virtual member function. Its behavior therefore does not address the question from the title. – MSalters Jun 23 '10 at 08:54
3

The pointer is valid, however you have to keep in mind that when a virtual function is invoked through a pointer it is always resolved in accordance with the dynamic type of the object used on the left-hand side. This means that when you invoke a virtual function from the constructor, it doesn't matter whether you invoke it directly or whether you invoke it through a pointer. In both cases the call will resolve to the type whose constructor is currently working. That's how virtual functions work, when you invoke them during object construction (or destruction).

Note also that pointers to member functions are generally not attached to specific functions at the point of initalization. If the target function is non-virtual, they one can say that the pointer points to a specific function. However, if the target function is virtual, there's no way to say where the pointer is pointing to. For example, the language specification explicitly states that when you compare (for equality) two pointers that happen to point to virtual functions, the result is unspecified.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
1

I have found a little explanation on the Old New Thing (a blog by Raymond Chen, sometimes referred to as Microsoft's Chuck Norris).

Of course it says nothing about the compliance, but it explains why:

B b;

b.A::vmember(); // [1]

(b.*&A::vmember)(); // [2]

1 and 2 actually invoke a different function... which is quite surprising, really. It also means that you can't actually prevent the runtime dispatch using a pointer to member function :/

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
0

I think no. Pointer to virtual member function is resolved via VMT, so the same way as call to this function would happen. It means that it is not valid, since VMT is populated after constructor finished.

Andrey
  • 59,039
  • 12
  • 119
  • 163
  • Note that the question explicitly asks only whether the pointer is valid, not calling it within the constructor. The function pointer is invoked after the constructor has finished and the vtable is complete – anorm Jun 22 '10 at 12:01
  • 2
    but what is a valid pointer that is invalid to call. Either it is valid - then you may call it, or it is invalid and you may not call it. Anyway - why is a pointer needed that is invalid and will not be called? – Tobias Langner Jun 22 '10 at 12:06
  • 2
    It will be called, just not from the constructor. The m_pMember is called from the test() function in the example – anorm Jun 22 '10 at 12:08
  • @Andrey: Absolutely incorrect. There are no limitations imposed on virtual function invocation from the constructor, regardless of whether it is done through a pointer or directly. The only thing to remember is that the dynamic type of the object is the type whose constructor is currently working. – AnT stands with Russia Jun 22 '10 at 18:07
  • @AndreyT "virtual function invocation from the constructor" there are, and i will try to find – Andrey Jun 22 '10 at 18:16
  • @AndreyT here you are: http://stackoverflow.com/questions/962132/calling-virtual-functions-inside-constructors then just google "calling virtual functions from constructor" and you will see a lot of reasons why you should not do that. – Andrey Jun 22 '10 at 18:18
  • ... and there are no limitations imposed on function pointer initialization in the constructor, regardless of where these pointers point to. – AnT stands with Russia Jun 22 '10 at 18:18
  • @Andrey: You must have misunderstood something. There are *NO* reasons why you shouldn't call virtual functions from the constructor. You just have to remember how virtual function work, when you invoke from the constructor (or destructor). As long as you don't have any unrealistic expectations, calling virtual functions from the constructor is perfectly fine. – AnT stands with Russia Jun 22 '10 at 18:20
  • @AndreyT i might not be right about validity of pointer, but in my comment i argued with statement "There are no limitations imposed on virtual function invocation from the constructor" – Andrey Jun 22 '10 at 18:20
  • @Andrey: What I'm saying is that it is perfectly legal and valid to invoke virtual functions from the constructor, as long as you know what it is doing and as long as you understand what you are doing. – AnT stands with Russia Jun 22 '10 at 18:23
  • @AndreyT you think that it is perfectly fine but experts don't http://www.artima.com/cppsource/nevercall.html – Andrey Jun 22 '10 at 18:24
  • @Andrey: Scott Meyers made a very good explanation, but drew a completely bogus conclusion out of it. I would say that the article you linked is intended for newbies, for whom the radical "Don't do it!!!" advice might be useful. But any more advanced C++ programmer knows that the conclusion Scott makes in his article is totally bogus. The proper advice sounds differently: "When you call virtual function from the constructor, make sure you know what will happen". – AnT stands with Russia Jun 22 '10 at 18:28
  • @AndreyT i fully understand what will happen if i invoke virtual function, but still i don't think that it is good idea. also i like to follow guidelines from experts. i don't think that "advanced" c++ means hacking and abusing language. it guides to instable and badly maintainable code. – Andrey Jun 22 '10 at 18:34
  • @Andrey: There's absolutely no "hacking" or "abusing" in calling virtual function from the constructor. The behavior of virtual functions (and pointers to them) in such cases is strictly, clearly and unambiguously defined by the language standard. What "hacking" are you talking about? – AnT stands with Russia Jun 22 '10 at 18:39
-1

IMO it is implementation defined to take address of a virtual function. This is because virtual functions are implemented using vtables which are compiler implementation specific. Since the vtable is not guaranteed to be complete until the execution of the class ctor is done, a pointer to an entry in such a table (virtual function) may be implementation defined behavior.

There is a somewhat related question that I asked on SO here few months back; which basically says taking address of the virtual function is not specified in the C++ standard.

So, in any case even if it works for you, the solution will not be portable.

Community
  • 1
  • 1
Abhay
  • 7,092
  • 3
  • 36
  • 50
  • -1, No. Nothing is _implementation defined_ unless the standard explicitly requires implementations to document their behavior. It can't be unspecified behavior either, as there is no range of reasonable behaviors. – MSalters Jun 22 '10 at 13:40
  • @MSalters: O.K., so would you term taking address of virtual functions undefined behavior? – Abhay Jun 22 '10 at 14:13
  • @Abhay: Not true. There nothing implementation defined about virtual functions, even when they are invoked through pointers. – AnT stands with Russia Jun 22 '10 at 18:08
  • @AndreyT: Please clarify 'there is nothing implementation defined about virtual functions'. I think the way they are implemented (vtables) itself is implementation defined. – Abhay Jun 22 '10 at 19:05
  • @Abhay: Details of implementations are always "implementation defined", no argument here :) What I'm saying is that the *behavior* of virtual functions and pointers to them in C++ language is *not* implementation defined. As for how this behavior is implemented - it doesn't matter at all. – AnT stands with Russia Jun 22 '10 at 19:08
  • @AndreyT: O.K. I agree that my reasoning may not be an accurate answer to OP's question as to which function will be called, but taking address of virtual functions surely seems dicey to me. The post i linked-to which deals with taking address of virtual functions, seems to suggest that it is *implementation defined*. – Abhay Jun 22 '10 at 19:14
  • @Abhay: Well... In reality the *behavior* in situations when the address of virtual function is taken (and the function is called through the pointer) is not implementation defined. The behavior is strictly and clearly defined be the standard. – AnT stands with Russia Jun 22 '10 at 19:17
  • How virtual functions are implemented behind the scenes is (as usual) unspecified - there's a range of possible solutions, and an implementation does **not** need to document which one they use. – MSalters Jun 23 '10 at 08:51