CRTP access

627 views
Skip to first unread message

David Krauss

unread,
Jun 14, 2014, 12:38:41 AM6/14/14
to std-pr...@isocpp.org
A CRTP base class must either be publicly inherited, or friended, so that within its own context the base is an accessible base of the derived class. Otherwise, the critical base-to-derived "downcast" is ill-formed.

Public inheritance is inappropriate unless the CRTP mixin is a public interface.

Friending is inappropriate unless the CRTP mixin should access non-public members. It adds boilerplate. Worst, it's impossible in multi-level CRTP: the derived class must know the specific base wanting friendship. Like many cases of friendship, it fundamentally violates encapsulation.

What could be done to make a base accessible to itself from derived classes? This is the reciprocal of protected inheritance.

I wonder, what harm could come of granting access to every base class subobject of every class within its own scope?

struct b {
    struct bn {
        template< typename d >
        static void foo( d & o ) {
            static_cast< b & >( o );
        }
    };
};

struct d : private b {} od;

b::bn::foo( od ); // Why not? b already presumes d::b exists and bn is just part of b.

Ville Voutilainen

unread,
Jun 14, 2014, 8:36:27 AM6/14/14
to std-pr...@isocpp.org
On 14 June 2014 07:38, David Krauss <pot...@gmail.com> wrote:
> A CRTP base class must either be publicly inherited, or friended, so that
> within its own context the base is an accessible base of the derived class.
> Otherwise, the critical base-to-derived "downcast" is ill-formed.
>
> Public inheritance is inappropriate unless the CRTP mixin is a public
> interface.
>
> Friending is inappropriate unless the CRTP mixin should access non-public
> members. It adds boilerplate. Worst, it's impossible in multi-level CRTP:
> the derived class must know the specific base wanting friendship. Like many
> cases of friendship, it fundamentally violates encapsulation.
>
> What could be done to make a base accessible to itself from derived classes?
> This is the reciprocal of protected inheritance.
>
> I wonder, what harm could come of granting access to every base class
> subobject of every class within its own scope?

The harm is that a derived class that inherits a base B privately is-not-a-B,
the only classes that can convert derived to B is derived itself and
its friends,
and sure as hell downcasting B to derived makes little sense if derived inherits
B privately.

I can easily find a way to do CRTP with non-public bases: add a virtual function
to B that returns T*, and have derived classes override it. Example:

#include <iostream>
template <class T> struct B
{
virtual T* pretty_please() = 0;
void f() {pretty_please()->g();}
};

struct D : private B<D>
{
D* pretty_please() override {return this;}
void g() {std::cout << "D::b" << std::endl;}
void h() {f();}
};

int main()
{
D d;
d.h();
}

David Krauss

unread,
Jun 14, 2014, 11:05:05 AM6/14/14
to std-pr...@isocpp.org
On 2014–06–14, at 8:36 PM, Ville Voutilainen <ville.vo...@gmail.com> wrote:

The harm is that a derived class that inherits a base B privately is-not-a-B,
the only classes that can convert derived to B is derived itself and
its friends,

That simply states the rule in question.

and sure as hell downcasting B to derived makes little sense if derived inherits
B privately.

If B inherently knows itself to be a base of D, then it’s not really very private. In CRTP the inheritance information is passed explicitly through the template parameter, so there’s no secret to keep.

I’m not suggesting to open all of D to B, but only to expose the base class subobject *to itself*.

I can easily find a way to do CRTP with non-public bases: add a virtual function
to B that returns T*, and have derived classes override it. Example:

No way. Avoiding virtual dispatch is a main advantage of CRTP.

If it can be done with such a “dynamic downcast,” why not support a more efficient semantic equivalent?

Ville Voutilainen

unread,
Jun 14, 2014, 4:57:19 PM6/14/14
to std-pr...@isocpp.org
On 14 June 2014 18:04, David Krauss <pot...@gmail.com> wrote:
> On 2014–06–14, at 8:36 PM, Ville Voutilainen <ville.vo...@gmail.com>
> wrote:
> The harm is that a derived class that inherits a base B privately
> is-not-a-B,
> the only classes that can convert derived to B is derived itself and
> its friends,
> That simply states the rule in question.

The "rule in question" doesn't talk about a higher-level design concept which
"is-a" represents.

>
> and sure as hell downcasting B to derived makes little sense if derived
> inherits
> B privately.
>
>
> If B inherently knows itself to be a base of D, then it’s not really very

B doesn't "inherently know" that it happens to be a base of its template
argument.

> private. In CRTP the inheritance information is passed explicitly through
> the template parameter, so there’s no secret to keep.

Except the secrets D keeps by design - it's not a B, and it's not convertible
from B nor to B. Base classes do not get special treatment and can't subvert
the access control of derived classes any more than any other classes can.

> I’m not suggesting to open all of D to B, but only to expose the base class
> subobject *to itself*.

I don't know that that's supposed to mean.

>
> I can easily find a way to do CRTP with non-public bases: add a virtual
> function
> to B that returns T*, and have derived classes override it. Example:
> No way. Avoiding virtual dispatch is a main advantage of CRTP.

You don't have to perform that virtual dispatch on every call. Alternatively,
you can pass the pointer to the derived object up to the base.

> If it can be done with such a “dynamic downcast,” why not support a more
> efficient semantic equivalent?


Because it breaks design exceptions without sufficient motivation to do so?

Richard Smith

unread,
Jun 14, 2014, 6:06:02 PM6/14/14
to std-pr...@isocpp.org
On Sat, Jun 14, 2014 at 5:36 AM, Ville Voutilainen <ville.vo...@gmail.com> wrote:
On 14 June 2014 07:38, David Krauss <pot...@gmail.com> wrote:
> A CRTP base class must either be publicly inherited, or friended, so that
> within its own context the base is an accessible base of the derived class.
> Otherwise, the critical base-to-derived "downcast" is ill-formed.
>
> Public inheritance is inappropriate unless the CRTP mixin is a public
> interface.
>
> Friending is inappropriate unless the CRTP mixin should access non-public
> members. It adds boilerplate. Worst, it's impossible in multi-level CRTP:
> the derived class must know the specific base wanting friendship. Like many
> cases of friendship, it fundamentally violates encapsulation.
>
> What could be done to make a base accessible to itself from derived classes?
> This is the reciprocal of protected inheritance.
>
> I wonder, what harm could come of granting access to every base class
> subobject of every class within its own scope?

The harm is that a derived class that inherits a base B privately is-not-a-B,
the only classes that can convert derived to B is derived itself and
its friends,
and sure as hell downcasting B to derived makes little sense if derived inherits
B privately.

I can easily find a way to do CRTP with non-public bases: add a virtual function
to B that returns T*, and have derived classes override it.

Or use a C-style cast from T* to B*. C-style derived-to-base casts ignore whether the base class is accessible (see [expr.cast]p4).
 
Example:

#include <iostream>
template <class T> struct B
{
    virtual T* pretty_please() = 0;
    void f() {pretty_please()->g();}
};

struct D : private B<D>
{
    D* pretty_please() override {return this;}
    void g() {std::cout << "D::b" << std::endl;}
    void h() {f();}
};

int main()
{
    D d;
    d.h();
}

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

David Krauss

unread,
Jun 14, 2014, 11:17:56 PM6/14/14
to std-pr...@isocpp.org
On 2014–06–15, at 6:06 AM, Richard Smith <ric...@metafoo.co.uk> wrote:

Or use a C-style cast from T* to B*. C-style derived-to-base casts ignore whether the base class is accessible (see [expr.cast]p4).

Ah, thanks! I forgot this. Fits perfectly. Certainly needs a code comment, and the language would probably benefit from CRTP-specific access specifiers, but the problem just got smaller.

(For folks following along, C casts degenerate to reinterpret_cast when the target is not in the inheritance hierarchy, so a static_assert on std::is_base_of, which also ignores accessibility, adds significant safety.)

inkwizyt...@gmail.com

unread,
Jun 15, 2014, 8:35:52 AM6/15/14
to std-pr...@isocpp.org
This wont be UB and only work when this class is first of base classes?

David Krauss

unread,
Jun 15, 2014, 9:33:36 AM6/15/14
to std-pr...@isocpp.org
No, the purpose of the static_assert I mentioned is to prevent reinterpret_cast from happening.

What we want is an access-bypassed static_cast semantic.


Diggory Blake

unread,
Jun 18, 2014, 2:12:26 PM6/18/14
to std-pr...@isocpp.org
I don't get it, why don't you just require the derived class to friend its base?

#include <iostream>

template <class T> struct B
{

   
void f();
};


struct D : private B<D>
{

   
friend class B<D>;
   
   
void g() {std::cout << "D::g" << std::endl;}
   
void h() {f();}
};

template<class T> void B<T>::f() {
   
static_cast<T*>(this)->g();

}

int main()
{
    D d
;
    d
.h();
}

Doesn't that bypass access control in exactly the way you wanted?

David Rodríguez Ibeas

unread,
Jun 18, 2014, 3:26:30 PM6/18/14
to std-pr...@isocpp.org
I am trying to wrap my head around the whole CRTP with a private base. If this is a private base, the implication is that the CRTP class will only be accessible from the derived type, right? If that is the case, only the derived type or a friend of it can call on CRTP member functions... This is not the CRTP I am used to see, but if we follow that, then why not have the functions be static and have the derived type pass the pointer?

template <typename T>
struct CRTP {
   static void foo(T *t) { t->bar(); }
};
class Derived : CRTP<Derived> {
   void bar();
   void call() {
       foo(this);
   }
};

But I am not sure I completely understand what the use cases for CRTP with private inheritance are.

    David

P.S. I have used CRTP with private inheritance to provide operators to types, but in this case there are no casts involved:

template <typename T>
struct relops {
    friend bool operator>(T const &a, T const &b) {
        return b < a;
    }
// ... other operators: <=, >= ...
};
class MyType : relops<MyType> {
   ...
};
bool operator<(MyType const &a, MyType const &b) { ... }

David Krauss

unread,
Jun 18, 2014, 9:36:05 PM6/18/14
to std-pr...@isocpp.org
On 2014–06–19, at 3:26 AM, David Rodríguez Ibeas <dib...@ieee.org> wrote:

I am trying to wrap my head around the whole CRTP with a private base. If this is a private base, the implication is that the CRTP class will only be accessible from the derived type, right? If that is the case, only the derived type or a friend of it can call on CRTP member functions... This is not the CRTP I am used to see, but if we follow that, then why not have the functions be static and have the derived type pass the pointer?

My use case involves protected access, not private, but you might have a point. Reviewing my code, adding the C-style cast didn’t really buy much. Here is my generic crtp_base class:

template< typename derived >
class crtp_base {
    void check() const
        { static_assert ( std::is_base_of< crtp_base, derived >::value, "Non-CRTP use of base." ); }
protected:
    derived & derived_this()
        { check(); return ( derived & ) * this; }
    derived const & derived_this() const
        { check(); return ( derived const & ) * this; }
};

This class only provides derived_this, intended for any CRTP base of derived. As a public interface, derived_this is useless so it’s protected. Does the same argument apply to class crtp_base?

crtp_base< derived > ==access?=> mixin< derived > ==public==> derived

The C cast allows me to choose protected access for crtp_base. It’s cleaner and consistent, but a bit pointless since the entire interface is protected anyway. In retrospect, I should probably revert it back to public inheritance and static_cast.

Usually, a mixin knows whether it’s going to be useful to the public or only to the inheriting class, and it can define the its own interface such that public inheritance is correct. Only when the derived class “knows better” should it apply a stronger qualification. Even then, qualifying the inherited interface with using declarations is almost as clean as qualifying the inheritance itself.

I do think the language fails to provide good qualification for the interface provided by a derived class to its CRTP base. Why not friendship? …


On 2014–06–19, at 2:12 AM, Diggory Blake <dig...@googlemail.com> wrote:

I don't get it, why don't you just require the derived class to friend its base?

Doesn't that bypass access control in exactly the way you wanted?

Since friendship cannot be transitive, a friended class must not delegate its functionality. friend B<D>; buys you exactly one mixin, but the pattern doesn’t work for an extended mixin derived from another, or a composite mixin derived from two others.

But, this is a different issue from my original post. “Exactly” what I wanted in that case was only that the CRTP cast itself would work, without suggesting that a derived-to-base detail interface exists as well.

Probably, the solution to the new issue is explicitly transitive friendship, irrespective of inheritance. I’m sure that design space has been explored before. Although my current inheritance hierarchy is leaking a little public access due to this very issue, I’m not inclined to attack it right now :P .

Reply all
Reply to author
Forward
0 new messages