tags:

views:

234

answers:

5

A co-worker asked about some code like this that originally had templates in it.

I have removed the templates, but the core question remains: why does this compile OK?

#include <iostream>

class X
{
public:
     void foo() { std::cout << "Here\n"; }
};

typedef void (X::*XFUNC)() ;

class CX
{
public:
    explicit CX(X& t, XFUNC xF) : object(t), F(xF) {}      
    void execute() const { (object.*F)(); }
private:
    X& object;
    XFUNC F;
}; 

int main(int argc, char* argv[])
{   
    X x; 
    const CX cx(x,&X::foo);
    cx.execute();
    return 0;
}

Given that CX is a const object, and its member function execute is const, therefore inside CX::execute the this pointer is const.

But I am able to call a non-const member function through a member function pointer.

Are member function pointers a documented hole in the const-ness of the world?

What (presumably obvious to others) issue have we missed?

+6  A: 

In this context object is a reference to a X, not a reference to a const X. The const qualifier would be applied to the member (i.e. the reference, but references can't be const), not to the referenced object.

If you change your class definition to not using a reference:

// ...
private:
    X object;
// ...

you get the error you are expecting.

Georg Fritzsche
Still, with `const`-methods one should only be able to call `const`-methods on member-objects, isn't that right?
Space_C0wb0y
The member is the reference, not the referenced object.
Georg Fritzsche
+5  A: 

The constness of execute() only affects the this pointer of the class. It makes the type of this a const T* instead of just T*. This is not a 'deep' const though - it only means the members themselves cannot be changed, but anything they point to or reference still can. Your object member already cannot be changed, because references cannot be re-seated to point to anything else. Similarly, you're not changing the F member, just dereferencing it as a member function pointer. So this is all allowed, and OK.

The fact that you make your instance of CX const doesn't change anything: again, that refers to the immediate members not being allowed to be modified, but again anything they point to still can. You can still call const member functions on const objects so no change there.

To illustrate:

class MyClass
{
public:
    /* ... */

    int* p;

    void f() const
    {
        // member p becomes: int* const p
        *p = 5;   // not changing p itself, only the thing it points to - allowed
        p = NULL; // changing content of p in const function - not allowed
    }
};
AshleysBrain
Matthieu M.
+3  A: 

The instance object of class X is not const. It is merely referenced by an object which is const. Const-ness recursively applies to subobjects, not to referenced objects.

By the alternative logic, a const method wouldn't be able to modify anything. That is called a "pure function," a concept which doesn't exist in current standard C++.

Potatoswatter
+1  A: 

You are calling foo on object, not on this.

Since object is declared as an X&, in a constant CX, it is actually an X& const (which is not the same as const X&) allowing you to call non const methods on it.

Vincent Robert
A: 

One helpful way of thinking about it might be that your X object is not a member of CX at all.

Noah Roberts
You could think about it like this, but it doesn't matter if it is or isn't. That class could have an X instance (`object`) as a member and also a reference member to refer to that. Then you'll find that you can't do `(object.*F)();`, but can still do `(reference.*F)();` even though it references the exact same thing that **is** a member of CX.
UncleBens