문제

I am a little confused why there is so much "hate" on the Curiously Recurring Template Pattern (CRTP) design pattern, for example I was reading "Game Programming Gems 3", and there is a design in there called the autoLists. This uses the CRTP to create an array of each type of object.

My question:

Why is this a bad thing to do? Specially aimed at the AutoLists idea but an answer about CRTP in general will be adequate.

My intention is to use it in an entity compontent system, so that I can separate each type of component easily.

도움이 되었습니까?

해결책 2

I've used CRTP and some variants of it extensively both in C++, Java and C#, and from "coworkers feedback" I can tell you one thing: many people simply don't understand it, and automatically get hostile towards "such overly complicated crap".

Until someone uses it a few times, people really find it hard to see benefits of it - just like with any other "complicated" "new" mechanism they see.

That's true that sometimes it is used in wrong places, and that it must be used with extra care to details - but that's life of any nontrivial tool. Just like with multipleinheritance - many hate it. But how can you hate a hammer? There's nothing to hate, just use it properly and in places where it is truly beneficial, not just because you can.

First, rethink if you really need to use it. Does the template base class really need to know the exact deriving type? Are virtual members not enough? Can you get away without it? What are the benefits in your case? Will it make the "higher level code" shorter, more readable, more obvious or more extensible or less error prone?

In many cases, you will find that the base does not need to know the exact derived type, and you can replace it with few more virtual methods. But that could make the overall code more complicated to further users. On the other hand, with CRTP, the final mechanism are more .. 'automagical', which sometimes is actually NOT beneficial.

In case of entity classes, often some variants of CRTP actually have a reason: if your base exposes some utility methods that return "similar" objects, you often want those methods to return refined "MyObject*" not "ObjectBase*", and that's hard to achieve without it. But, the real question is: should those methods really be in the entity's base class instead of inside the 'factory', 'manager' or 'storagecontext'?

다른 팁

Inheritance in C++ has served two distinct purposes:

  1. Mixins (adding new, drop-in behavior to a class, without duplicating code).
    In this scenario, the base class has little meaning of its own -- its purpose is to support the new behavior, and not to be used as a common base class among all the subclasses.

  2. Polymorphism (extending already-declared behavior in the base class).
    In this scenario, the base class provides a common interface for all subclasses, yada yada.

CRTP is generally used for the first purpose, and virtual is used for the second.
Recognizing the difference between the two isn't easy and takes some practice.

Sometimes, you can achieve the same thing with both -- and the difference is only in whether the "polymorphism" is static (at compile-time) or dynamic (at run-time).
If you don't need run-time polymorphism then you generally go with CRTP because it's usually faster, as the compiler can see what's going on at compile time.

That said, CRTP is used widely enough that I would be hesitant to say there is "so much hate" on it.

CRTP introduce restrictions on the usage of an CRTP class that the compiler can not check, i.e. it may not be type safe. Take the following as an example.

#include <iostream>
using namespace std;

class Base {
public:
    virtual ~Base() {}
    virtual Base *copy() = 0;
    virtual void SayHello() = 0;
};

template <typename Derived>
class BaseCopy: public Base {
public:
    virtual Base *copy()
    {
        return new Derived(static_cast<Derived const&>(*this));
    }
};

If the user of the Base class does not know about the restricted usage and declares

class Bar: public BaseCopy<Bar> { public: void SayHello(void) { cout << "Hello, I am Bar\n";} };
class Foo: public BaseCopy<Bar> { public: void SayHello(void) { cout << "Hello, I am Foo\n";} };

int main(void)
{
    Foo *foo = new Foo;
    Base *foo2 = foo->copy(); // What is foo2?
    foo->SayHello();
    foo2->SayHello();
    delete foo2;
    delete foo;

    return 0;
}

Compiling this with eg. g++

g++ -Wall -g main.cpp -o CRTP-test.exe

will compile without problem but calling foo->copy(); will invoke undefined behaviour since the result will be a Bar constructed from a Foo.

//jk

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top