简体   繁体   English

奇怪的重复模板模式(CRTP),AutoLists和C ++

[英]Curiously Recurring Template Pattern (CRTP), AutoLists and C++

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. 我有点困惑为什么在好奇的重复模板模式(CRTP)设计模式上有如此多的“讨厌”,例如我正在阅读“Game Programming Gems 3”,并且有一个名为autoLists的设计。 This uses the CRTP to create an array of each type of object. 这使用CRTP创建每种类型对象的数组。

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. 特别针对AutoLists的想法,但一般来说CRTP的答案就足够了。

My intention is to use it in an entity compontent system, so that I can separate each type of component easily. 我的目的是在实体组件系统中使用它,这样我就可以轻松地分离每种类型的组件。

Inheritance in C++ has served two distinct purposes: C ++中的继承有两个不同的目的:

  1. Mixins (adding new , drop-in behavior to a class, without duplicating code). Mixins(向类添加新的 ,drop-in行为,不重复代码)。
    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. 在这种情况下,基类为所有子类yada yada提供了一个通用接口。

CRTP is generally used for the first purpose, and virtual is used for the second. CRTP通常用于第一个目的, virtual用于第二个目的。
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. 如果你不需要运行时多态,那么你通常会使用CRTP,因为它通常更快,因为编译器可以看到编译时发生了什么。

That said, CRTP is used widely enough that I would be hesitant to say there is "so much hate" on it. 也就是说,CRTP被广泛使用,我会犹豫不决地说它有“如此多的仇恨”。

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". 我已经在C ++,Java和C#中广泛使用了CRTP及其一些变体,并且从“同事反馈”中我可以告诉你一件事:许多人根本不理解它,并且自动变得对“这样过于复杂”持怀疑态度废话”。

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. 另一方面,对于CRTP,最终的机制更多是“自动化”,有时实际上并不是有益的。

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. 对于实体类,通常CRTP的某些变体实际上有一个原因:如果你的基础暴露了一些返回“类似”对象的实用方法,你经常希望这些方法返回精炼的“MyObject *”而不是“ObjectBase *”,那就是没有它很难实现。 But, the real question is: should those methods really be in the entity's base class instead of inside the 'factory', 'manager' or 'storagecontext'? 但是,真正的问题是:这些方法真的应该在实体的基类中而不是在“工厂”,“经理”或“存储上下文”中吗?

CRTP introduce restrictions on the usage of an CRTP class that the compiler can not check, ie it may not be type safe. CRTP引入了对编译器无法检查的CRTP类的使用的限制,即它可能不是类型安全的。 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 如果Base类的用户不知道受限制的使用并声明

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(); 将编译没有问题,但调用foo->copy(); will invoke undefined behaviour since the result will be a Bar constructed from a Foo. 将调用未定义的行为,因为结果将是从Foo构造的Bar。

//jk // JK

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么奇怪的重复模板模式(CRTP)的工作原理 - why Curiously Recurring Template Pattern (CRTP) works 什么是奇怪的重复模板模式(CRTP)? - What is the curiously recurring template pattern (CRTP)? 如何在C ++中强制使用奇怪的重复模板模式 - How to force use of curiously recurring template pattern in C++ 使用Clang中的静态constexpr奇怪地重现模板模式(CRTP) - Curiously recurring template pattern (CRTP) with static constexpr in Clang 奇怪的重复模板模式(CRTP)和派生的构造函数参数 - Curiously recurring template pattern (CRTP) and derived constructor arguments CRTP(Curious Recurring Template Pattern)中的模板派生类 - Templated derived class in CRTP (Curiously Recurring Template Pattern) 奇怪的重复模板模式多态拷贝(C ++)中的继承 - Inheritance in curiously recurring template pattern polymorphic copy (C++) 好奇的重复模板模式(CRTP)是否是正确的解决方案? - Is the Curiously Recurring Template Pattern (CRTP) the right solution here? 使用奇怪的重复模板模式(CRTP)和其他类型参数 - Use Curiously Recurring Template Pattern (CRTP) with additional type parameters C ++ BigIntegers和奇怪的重复模板模式问题 - C++ BigIntegers and the Curiously Recurring Template Pattern Issue
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM