views:

205

answers:

6

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?

A: 

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
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
Dr. Sbaitso
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
It will be called, just not from the constructor. The m_pMember is called from the test() function in the example
Dr. Sbaitso
@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.
AndreyT
@AndreyT "virtual function invocation from the constructor" there are, and i will try to find
Andrey
@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
... and there are no limitations imposed on function pointer initialization in the constructor, regardless of where these pointers point to.
AndreyT
@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.
AndreyT
@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
@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.
AndreyT
@AndreyT you think that it is perfectly fine but experts don't http://www.artima.com/cppsource/nevercall.html
Andrey
@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".
AndreyT
@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
@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?
AndreyT
+1  A: 

Read this article for an in-depth discussion of member function pointers and how to use them. This should answer all your questions.

Berkus
http://meta.stackoverflow.com/questions/13369/is-it-okay-to-answer-a-stackoverflow-question-with-a-link
KennyTM
Besides, it's an old article - for instance, it addresses MSVC4-7. This question is explicitly about _compliant_ compilers.
MSalters
That's a lot of text to read when what was requested was a simple yes or no answer. So which is it?
Rob Kennedy
It depends very much on your inheritance graph, but generally you will get a valid _offset_ from the start of the object, not full pointer.
Berkus
Article may be old, but it addresses old compilers for the sake of workarounds. Grab the code and see for yourself. It's amusing if not educational.
Berkus
A: 

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.

Abhay
-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
@MSalters: O.K., so would you term taking address of virtual functions undefined behavior?
Abhay
@Abhay: Not true. There nothing implementation defined about virtual functions, even when they are invoked through pointers.
AndreyT
@AndreyT: Please clarify 'there is nothing implementation defined about virtual functions'. I think the way they are implemented (vtables) itself is implementation defined.
Abhay
@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.
AndreyT
@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
@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.
AndreyT
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
+3  A: 

"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
Matthieu M.
Should be `(a.*test)()`. Same for `b`.
AndreyT
@AndreyT: fixed.
MSalters
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
+1  A: 

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 :/

Matthieu M.
You probably meant that you can't disable the run-time dispatch when calling a member function through a pointer.
AndreyT
I mean that "you can't actually invoke the non-virtual function using a pointer to member function" sounds a bit misleading to me.
AndreyT
I corrected it, thanks.
Matthieu M.
+2  A: 

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.

AndreyT