c++ - 有什么比元工厂更好的解决构造函数注入(inject)到 CRTP 中的派生类的问题吗?

标签 c++ qt c++11

CRTP ,我想干净利落地将构造函数注入(inject)派生类——不使用宏,也不写出来。这似乎是不可能的,所以我想出了一些解决方法。

首先,有一个基础 event class (QEvent)每个派生类 ( see rationale ) 应该有一个唯一的整数类型标签。您可以通过调用注册函数来获取它。创建一个 CRTP 包装器可以很容易地向您隐藏它:

template <typename Derived> class EventWrapper : public QEvent {
public:
    EventWrapper() : QEvent(staticType()) {}
    static QEvent::Type staticType() {
        static QEvent::Type type = static_cast<QEvent::Type>(registerEventType());
        return type;
    }
};
class MyEvent1 : public EventWrapper<MyEvent1> {}; // easy-peasy
class MyEvent2 : public EventWrapper<MyEvent2> {};

请注意 MyEvent1::staticType() != MyEvent2::staticType(): registerEventType() 每次调用时都会返回唯一的类型。

现在我想让事件类携带一些数据:

template <typename Derived> class StringEvent : public EventWrapper<D> {
    std::string m_str;
public:
    explicit StringEvent(const std::string & str) : m_str(str) {}
    std::string value() const { return m_str; }
};

但是这里我们遇到了一个问题:我们需要在派生类的每个中手动定义构造函数。这里的重点是创建这样的类应该很容易,因为可能有许多不同的字符串携带事件类型。但这绝非易事:

class MyEvent3 : public StringEvent<MyEvent3> {
    public: MyEvent3(std::string s) : StringEvent(s) {}
};

这显然很快就变老了,即使使用 C++11 构造函数转发也是如此:

class MyEvent3 : public StringEvent<MyEvent3> { using StringEvent::StringEvent; };

我们想要的是一种将此构造函数注入(inject)派生类的方法,或者避免这样做同时仍然提供易用性。当然,您可以将它隐藏在预处理器宏中,但我讨厌那些宏,因为它们为非常简单的概念引入了新名称,因此维护起来很麻烦。

我们当然可以使用虚拟类型。请注意,不需要定义虚拟类型。它只是用作类型参数的名称。

// Pre-C++11
class DummyEvent3;
typedef StringEvent<DummyEvent3> MyEvent3;
// C++11
class DummyEvent3;
using MyEvent3 = StringEvent<DummyEvent3>;

另一种解决方案是使用 int 模板参数并使用枚举值,但这带回了使用 registerEventType() 解决的唯一性问题第一名。保证大型程序的正确性可不是一件有趣的事。而且您仍然需要拼出枚举。

所以,我想出了一个元程序类,我将其称为元工厂,它可以为我们生成随时可用的 StringEvent 类,同时将它们全部保持为一种类型定义:

// the metafactory for string events
template <typename Derived> class StringEventMF {
public:
    class Event : public EventWrapper<Derived> {
        std::string m_str;
    public:
        explicit Event(const std::string & val) : m_str(val) {}
        std::string value() const { return m_str; }
    };
};

或者只是

template <typename Derived> class StringEventMF {
public:
    typedef StringEvent<Derived> Event;
};

它的用法如下:

class Update : public StringEventMF<Update> {};
class Clear : public StringEventMF<Clear> {};

void test() {
   Update::Event * ev = new Update::Event("foo");
   ...
}

您使用的类是 Update::EventClear::EventUpdateClear 是元工厂:它们为我们生成所需的事件类。从元工厂派生回避了从具体类类型派生。元工厂类型提供了创建独特具体类类型所需的独特类型鉴别器。

问题是:

  1. 是否有任何“更清洁”或“更理想”的方式来做这件事?理想情况下,以下无效伪代码将是我理想的实现方式 - 零重复:

    class UpdateEvent : public StringEvent <magic>;
    

    派生类的名称只出现一次,基本概念 StringEvent 的名称也只出现一次。 CRTP 要求类名出现两次 - 到目前为止我认为这是可以接受的,但我的元编程功能已经破烂不堪。同样,我想要一个无预处理器的解决方案,否则就很容易了。

  2. metafactory 这个名字是我最初的发明(哈哈),还是仅仅是我的 google-Fu 所缺少的?这种元工厂模式似乎非常灵活。通过多重推导很容易组成元工厂。假设您想要一个工厂制作的 Update::Event,另一个工厂制作的 Update::Foo

这个问题的动机是 this answer .注意:在实际代码中,我会使用 QString,但我会尽量保持它的通用性。

最佳答案

我认为您正在寻找的可能只是使用 placement new 实例化基类。
派生类将不可构造,因为除非它们创建匹配的构造函数。
但是,它们不一定是可构建的,无论如何您都可以使用它们。 (它仍然可以被破坏)。

template <class T>
class Base
{
protected: Base(int blah) { }

public: static T* CreateInstance(int data) { 
            T* newOjectBlock =  reinterpret_cast<T*>(::operator new(sizeof(T))); // allocate enough memory for the derived class
            Base* newBasePlace = (Base*)(newOjectBlock); //point to the part that is reseved for the base class
            newBasePlace=  new ((char*)newBasePlace) Base(data); //call the placement new constrcutor for the base class
            return newOjectBlock;
        }
};

class Derived : public Base<Derived> {}

然后让CRTP基类这样构造派生类:

Derived* blah =  Derived::CreateInstance(666);

如果有人想要初始化派生类,他们应该创建一个匹配的构造函数来调用基类构造函数。
或者,只需创建一个初始化其成员的 .init() 方法,并将在创建实例后调用。

或者,我们可以想别的东西,这只是一个概念的想法。

关于c++ - 有什么比元工厂更好的解决构造函数注入(inject)到 CRTP 中的派生类的问题吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19187469/

相关文章:

c++ - 捕获位图快吃RAM

c++ - Qmake Qt : Move Header file to build folder

qt - 对 QGraphicsScene 使用 MouseMoveEvent,对 QGraphicsItem 使用 HoverEnterEvent

c++ - 为什么这个使用 std::rotate 的简单程序无法编译?

c++ - "undefined reference to ` vtable.. "- How to know which method isn' t 定义了吗?

c++ - 可以从静态函数调用构造函数吗?

Qt错误 "persistent model indexes corrupted"为什么?

c++ - 在 C++ Qt 中坚持使用 prepare() 和 bindvalue()

c++ - std::bind 涉及到临时类拷贝时的模板推导

c++ - 带有 std::shared_ptr 的视觉泄漏检测器