On Thursday, 11 February 2016 19:28:58 UTC+2, K. Frank wrote:
> Hello Group!
>
> To follow up on my previous post, I have also seen
> discussions where the CRTP (curiously recurring
> template pattern) is used, as I understand it, to
> define interfaces.
>
We often are in situation where base class needs to call functions of
derived class. Majority of other programming languages have chosen to
use virtual functions always in such a situation but C++ has CRTP.
CRTP provides two clear strengths, one is better efficiency and other
is faster feedback (compile time) about defects. It also provides
a major meta-weakness by increasing complexity of design since choice has
to be made design-time.
The choice between virtual functions or CRTP depends on if the
interrelations between run-time objects of resulting program are fixed
and known compile-time or not. On lot of cases these very likely are, on
few cases these certainly are not and on significant amount of cases it
can be uncertain design-time.
Therefore usage of virtual functions may be a premature pessimization
or usage of CRTP may be a premature optimization and if it happens
often then developer needs to gain skill in thinking about it first or
in refactoring between the two. ;)
>
> It's true that the class Interface serves to define the
> interface to be implemented, but having to manually map
> the interface to the implementation:
>
> void doA() { static_cast<D*>(this)->implementationA(); }
>
> seems annoying.
Unless I misunderstand you it looks rather little semantic
inconvenience that can be reduced with little mixin that provides
convenience helper overloads for that if you really bother.
void a() const { md().do_a(); }
Replace 'md()' with 'most_derived()' if abbreviations are forbidden
by codding standard.
Note that some developers ignore encapsulation and const-correctness
(use 'struct', everything non-const) and copy-paste code and that
results with wrong template arguments of CRTP:
struct foo : base<bar> { /* ... */ };
It is good idea to make that not to compile for example by making
something of 'base<bar>' non-accessible to 'foo' and idiomatically
it is private destructor:
template <typename T>
struct base
{
private:
~base() {}
friend T;
};
Note also that on lot of cases we want to do more things in implementation.
It may be for debugging purposes or because the gaps that derived class
fills do not match with external interface exactly 1:1:
void a() const { DBG("transaction a"); md().check(); md().do_a(); }
void b() { prepare_b(); md().do_b(); }
>
> My preference in a case like this would be to just define
> a class that "duck-types" the desired interface:
>
> struct C {
> void doA() { cout << "C::doA" << endl; }
> void doB() { cout << "C::doB" << endl; }
> void doC() { cout << "C::doC" << endl; }
> };
>
> and use it, e.g.:
>
> UseInterface<C> uc;
> uc.doStuff;
That is useful on lucky case when interface and gaps to fill match
exactly 1:1. The convenient external interface and gaps that derived
class is anticipated potentially to fill are usually rather different
and so your way results with violation of DRY (IOW several of
copy-paste-filled classes).
>
> Am I missing anything about the benefits of using the
> CRTP to define interfaces? If not, do you think that
> using the CRTP as a place to define an interface is
> worth the hassle?
I think that you consider the problem that CRTP solves as some sort of
close to 1:1 thin interface wrapper, and that is not anywhere frequent
case. You will get better idea what CRTP does by reading documentation
of libraries that use it in interface. There is large amount of
such libraries since the CRTP typically wins dynamic polymorphism in
performance and gives more compile-time diagnostics on case of misuse.
For example documentation of boost::iterator_facade explains it
quite well what is really typically expected to be going on:
|> iterator_facade and the operator implementations need to be able to
|> access the core member functions in the derived class. Making the
|> core member functions public would expose an implementation detail to
|> the user. The design used here ensures that implementation details
|> do not appear in the public interface of the derived iterator type.
http://www.boost.org/doc/libs/1_52_0/libs/iterator/doc/iterator_facade.html