Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function member pointer with private base

The following code yields a compile time error:

'base::print' : cannot access private member declared in class 'base_der'

However, I have made the member public in the derived class. Why doesn't this work?

#include <iostream>

using namespace std;

class base
{
public:
    int i;
    void print(int i)
    {
        printf("base i\n");
    }
};

class base_der : private base
{
public:
    using base::print;
};

int main()
{
    // This works:
    base_der cls;
    cls.print(10);

    // This doesn't:    
    void (base_der::* print)(int);
    print = &base_der::print; // Compile error here
}
like image 322
SerbanLupu Avatar asked Aug 04 '11 14:08

SerbanLupu


People also ask

How to access a member function by pointer in C++?

Create a class along with data member and member functions and then access the member functions by using a pointer in C++. To access a member function by pointer, we have to declare a pointer to the object and initialize it (by creating the memory at runtime, yes!

What is a pointer to a derived class?

A pointer to derived class is a pointer of base class pointing to derived class, but it will hold its aspect. This pointer of base class will be able to temper functions and variables of its own class and can still point to derived class object. Writing code in comment?

What is a pointer-to-member function?

It's a regular pointer to the one instance of the static member. Only one instance of a static member exists for all objects of a given class. That means you can use the ordinary address-of ( &) and dereference ( *) operators. Invoking a virtual function through a pointer-to-member function works as if the function had been called directly.

How do you call a member function in C++?

In C++ , function pointers when dealing with member functions of classes or structs, it is invoked using an object pointer or a this call. We can only call members of that class (or derivatives) using a pointer of that type as they are type safe.


3 Answers

I think there are a few interacting problems contributing to the error:

  1. pointer-to-member types have unintuitive type conversion characteristics
  2. the using declaration doesn't affect the type of the name brought into scope
  3. while the name base_der::print is accessible, the class base still isn't and in an attempt to convert a pointer-to-member, the actual type of the class in the pointer-to-member type is part of the consideration.

C++03 7.3.3 "The using declaration"

A using-declaration introduces a name into the declarative region in which the using-declaration appears. That name is a synonym for the name of some entity declared elsewhere.

Note that while the name is brought into the new 'region', it's a synonym - the type of what the name refers to is the same. So, I think that in your example, the name base_der::print has a type void (base::*)(int), not type void (base_der::*)(int).

The C++03 standard also says this about conversions between pointer-to-member types (4.11 "Pointer to member conversions"):

An rvalue of type "pointer to member of B of type cv T", where B is a class type, can be converted to an rvalue of type "pointer to member of D of type cv T", where D is a derived class (clause 10) of B. If B is an inaccessible (clause 11), ambiguous (10.2) or virtual (10.1) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion refers to the same member as the pointer to member before the conversion took place, but it refers to the base class member as if it were a member of the derived class. The result refers to the member in D’s instance of B. Since the result has type "pointer to member of D of type cv T", it can be dereferenced with a D object. The result is the same as if the pointer to member of B were dereferenced with the B sub-object of D.

Also note 7.3.3/13 "The using declaration" (emphasis added):

For the purpose of overload resolution, the functions which are introduced by a using-declaration into a derived class will be treated as though they were members of the derived class. In particular, the implicit this parameter shall be treated as if it were a pointer to the derived class rather than to the base class. This has no effect on the type of the function, and in all other respects the function remains a member of the base class.

Now, the code example that generates an error:

// This doesn't:    
void (base_der::* print)(int);
print = &base_der::print; // Compile error here

is trying to convert a "pointer to member of D" to a "pointer to member of B" - which is a conversion in the wrong direction. If you think about it for a moment, you'll realize why a conversion in this direction isn't safe. A variable of type "pointer to member of B" might not be used with an object that has anything to do with class D - but if you call a function with type "pointer to member of D" (which is what void (base_der::* print)(int) is), it'll rightly expect that the this pointer will be pointing to a D object.

Anyway, while I think that the root of the problem is this conversion problem, I think you're getting a complaint about the accessibility because when the compiler is trying to handle the conversion, it's first checking the accessibility of base - and even though the name base_der::print (which is an alias for base::print) is accessible because of the using declaration, class base still isn't.

Disclaimer: this analysis is coming from someone who has little experience in the nuances of pointer-to-member types. They're an area of C++ that is complex, difficult to use except in the simplest scenarios, and apparently have a lot of portability problems (see Doug Clugston's article, http://www.codeproject.com/KB/cpp/FastDelegate.aspx, which is old enough that a lot of these problems may have been addressed by now, but I suspect they aren't).

And when you say that something in C++ is one of the more complex or less-well-understood areas, that's saying a lot.

like image 187
Michael Burr Avatar answered Sep 18 '22 10:09

Michael Burr


I can't say I know why (nor can I speak to the spec), but clang's error message may be instructive:

error: cannot cast private base class 'base' to 'base_der'

So changing the type of the member function works, in clang and gcc at least:

void (base::* print)(int);
print = &base_der::print; // works!
like image 29
bosmacs Avatar answered Sep 22 '22 10:09

bosmacs


It's because,

class base_der : private base

Inheritance is private. So base is inaccessible to base_der. Change that to public and it will work.

like image 23
iammilind Avatar answered Sep 20 '22 10:09

iammilind