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
}
I think there are a few interacting problems contributing to the error:
- pointer-to-member types have unintuitive type conversion characteristics
- the using declaration doesn't affect the type of the name brought into scope
- while the name
base_der::print
is accessible, the classbase
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.
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!
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.
来源:https://stackoverflow.com/questions/6942949/function-member-pointer-with-private-base