Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access on base class of template prior to expansion of the template being derived from

I'm trying to publicly derive a class from a template that will make it inherit from a base class and get access on protected members. But before the template is expanded it doesn't have those rights, so it can't use a Base member as a template parameter:

using Fun = void (*)();

class Base {
protected:
    // friend class Derived; // ...need this to eliminate complaint
    static void something();
};

template<Fun F>
class Variant : public Base {};

class Derived : public Variant<&Base::something> { // `something()` is protected
public:
    void somethingElse() {
        something(); // doesn't complain about this `something()`
    }
};

int main() {}

The weird bit about this to me was that friending it worked at all. I wondered if I might "sneak Derived in the door" by putting a public virtual inheritance from Base before the Variant:

class Derived : public virtual Base, public Variant<&Base::something>

That didn't help.

Question: is there some other trick for avoiding explicit mention of all derived classes in Base, yet still have access to pick protected members out of it for template parameters?

(Note: Trying this on an older gcc, 4.6.3, looks like even the friending doesn't help in that case. So it seems support for that is somewhat new.)

like image 202
HostileFork says dont trust SE Avatar asked Nov 09 '22 03:11

HostileFork says dont trust SE


1 Answers

Stick the offending access into a metafunction. Derive the metafunction class from the base.

template<typename B>
struct something_variant : public B {
    typedef Variant< & B::something > type;
};

class Derived : public something_variant<Base>::type {
    …

http://coliru.stacked-crooked.com/a/6bca00455bd3daca

Regarding CWG 372, the critical text in the resolution is this:

[A]ccess checking of base-specifiers must be deferred until the entire base-specifier-list has been seen.

This was already accepted in C++11, so it's interesting that your example is rejected. And, plugging the relevant example code from the C++11 standard into recent Clang and GCC demonstrates that they simply did not implement the deferral. It's at least a little unsurprising, since implementation requires some data structure to represent a set of deferred access checks… fairly high effort for a corner case.

like image 117
Potatoswatter Avatar answered Nov 14 '22 22:11

Potatoswatter