Function member pointer with private base

别来无恙 提交于 2019-12-05 08:25:44

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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!