Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

CRTP and overhead

25 views
Skip to first unread message

bitrex

unread,
Feb 5, 2017, 10:56:40 AM2/5/17
to
I've read about the "CRTP" and how it's used as a way to implement
polymorphic structures instead of using inheritance and virtual
functions, etc.

I'm wondering what its time/space overhead savings is over vtable based
inheritance is? Understanding of course the the downside is that all the
"overloads" are resolved at compile-time, not runtime.

Is it zero overhead? How exactly does the compiler deal with a class
which inherits from another class which is templated on its own subclass?

It's of interest to me because it seems like a more efficient way to
"inject" common behavior into subclasses in the "Policy-based design"
paradigm:

https://en.wikipedia.org/wiki/Policy-based_design

without the use of traditional dynamic inheritance.

Alf P. Steinbach

unread,
Feb 5, 2017, 11:31:32 AM2/5/17
to
Just measure.

It's a bit up and down but overall CRTP (static polymorphism) wins on
size and loses on clarity and functionality.

To understand how the compiler deals with things, and hence how you need
to code things, view a class declaration like

struct Derived;

struct Blah: Some_base
{
void foo() { say( "h3110!" ); }
};

struct Derived: Blah { using X = int; };

as shorthand for

struct Derived;

struct Blah
{
void foo();
};

struct Derived: Blah { using X = int; };

inline void Blah::foo() { say( "h3110!" ); }

Here in the definition of Blah you can't use anything from Derived,
because it's an incomplete class at this point. It doesn't have a size,
the compiler can't look into it to find e.g. a type X you'd like to use.
In particular you can't use X in the declaration of foo().

But, within the /definition/ of foo, class Derived is complete, and can
be used, including that you can use type X.

That's sort of obvious after applying the transformation of separating
out the member function definitions, but it can be bewildering before
one realizes that that's how the compiler deals with things here.

Cheers & hth.,

- Alf

bitrex

unread,
Feb 12, 2017, 2:24:17 PM2/12/17
to
Thanks for your helpful reply - sorry for my delay in getting back.

A followup question: is there a better way to implement the CRTP when
you want to inject a static data structure into a derived class, so the
end result is that you get a new static structure for each variety of
template of the derived class you instantiate? Like so:

#include <array>
#include <iostream>

template <template <typename> class Derived, typename T>
struct Foo {

static std::array<T, 5>& my_array() {
static auto my_array = std::array<T, 5>();
return my_array;
}

std::array<T, 5>& get() {
return static_cast<Derived<T>*>(this)->get_impl();
}

};

template <typename T>
struct Bar : public Foo<Bar, T> {
std::array<T, 5>& get_impl() { return this->my_array(); }
};

int main()
{
auto baz = new Bar<int>();
auto bif = new Bar<short>();
baz->get() = {1, 2, 3, 4, 5};
bif->get() = {6, 7, 8, 9, 10};

for (auto i : baz->get())
{
std::cout << i << std::endl;
}

for (auto c : bif->get())
{
std::cout << c << std::endl;
}

return 0;
}

This works fine if you simply want to "inject" a single base class
template into the derived class, but gets real messy real quick if you
want to do a longer chain, or use multiple inheritance. Didn't know if
there's some way to automate it more cleanly...

Alf P. Steinbach

unread,
Feb 12, 2017, 4:28:08 PM2/12/17
to
On 12.02.2017 20:24, bitrex wrote:
>
> [snip]
Not sure exactly what you're asking but in the example the base class
provides a member function `get` that calls derived class `get_impl`
that accesses the base class' static array. That seems rather roundabout
unless you intend `get_impl` as a kind of customization hook. And with
`get_impl` doing something else, some customization, you will have
needlessly outfitted the derived class with a static array.

I think the easiest way to have an easily customizable static array in
each most derived class, is to declare it there.

But I may be way off, because I don't understand the purpose here.


Cheers!,

- Alf

bitrex

unread,
Feb 12, 2017, 5:19:04 PM2/12/17
to
Sure, that code was just a boilerplate example. The situation I had in
mind was where the static member in the inherited base class defines
some more complicated resource-holding structure, like an allocator or
policy, that's then injected into the derived class.

On little processors like some of the ARM Cortex series it's often nice
to have everything allocated statically at startup, and I was thinking
with the CRTP I could make a base class that defines some common type of
resource, and then use this type of "static polymorphism" so each
derived class template type gets its own (all with static storage
duration) copy of whatever that resource is to play with.
0 new messages