如何在 STL 容器中存储具有抽象模板类型的抽象模板 class?
How to store an abstract templated class with an abstract template type in STL container?
我希望标题没有误导,但我在一个问题中找不到合适的语言来定义我的问题。
最近我一直在尝试使用 C++ 实现 "CQRS Command Handler" 设计模式。我有 2 个层次结构,我必须结合在一起:
ICommand
struct ICommand
{
virtual ~ICommand() = default;
};
struct SampleCommand : ICommand
{
int sampleParameter;
SampleCommand() : sampleParameter(0)
{
}
explicit SampleCommand(const int sampleParameter)
{
this->sampleParameter = sampleParameter;
}
};
ICommandHandler
template<typename T, typename = std::enable_if_t<std::is_base_of<ICommand, std::decay_t<T>>::value>>
struct ICommandHandler
{
virtual void Handle(std::shared_ptr<T> command) = 0;
virtual ~ICommandHandler() = default;
};
class SampleCommandHandler : public ICommandHandler<SampleCommand>
{
public:
void Handle(std::shared_ptr<SampleCommand> command) override
{
std::cout << "sampleParameter " << command->sampleParameter << std::endl;
}
};
我需要实现的最后一部分是一个调度程序,它接受一个命令,找到一个处理程序并将命令委托给找到的处理程序。我想到的第一个想法是公开一些处理程序在调度程序中注册 API 并编写调度方法,该方法将简单地尝试 dynamic_cast 所有已注册的处理程序,如果某些转换成功,它将调用找到的处理程序作为如下所示:
class Dispatcher
{
public:
template<typename T>
void Dispatch(std::shared_ptr<T> command)
{
auto handler = std::find_if(std::begin(_handlers), std::end(_handlers), [](auto handler)
{
return dynamic_cast<ICommandHandler<T>*>(handler);
});
if(handler != std::end(_handlers))
{
(*handler)->Handle(command);
}
}
private:
std::vector<?> _handlers;
};
问题是:“_handlers”std::vector 应该存储什么类型才能使 Dispatcher::Dispatch 方法正常工作,如果这可能的话?
到目前为止我尝试过的:
std::vector< ICommandHandler *> - 未编译,因为无法将具体处理程序强制转换为 ICommandHandler< ICommand> *>。
Error C2440 'initializing': cannot convert from 'SampleCommandHandler *' to 'ICommandHandler<ICommand,void> *'
std::vector - 未编译,因为 dynamic_cast 无法应用于 void*
你在这里有一堆毫无意义的(至少对我来说)多态性;我会根据它们处理的内容拆分处理程序的容器,而不是使用一个向量。就像从类型索引到处理程序的 map/unordered 映射;或者,有一组固定的类型来处理。
但是要解决问题问:
struct ICommandHandlerBase{
virtual ~ICommandHandlerBase(){};
};
template<typename T, typename = std::enable_if_t<std::is_base_of<ICommand, std::decay_t<T>>::value>>
struct ICommandHandler:ICommandHandlerBase
{
现在存储 ICommandHandlerBase*
或 unique_ptr<ICommandHandlerBase>
的向量。
当然是用map或者无序map来查。如果必须,您可以使用带有自定义比较器的(无序)集合。虽然那不能回答你的问题:-)
假设您的处理程序确实是多态的,那么您的映射或集合必须是指向接口 ICommandHandler 的指针。它们不应该是原始指针,而可以是某种智能指针。
使用 std::shared_ptr 意味着处理程序对象的生命周期在处理事件时得到保证 - 如果它也需要 shared_ptr,即使事件从映射中删除(在处理程序内部或由另一个线程或处理程序),但增加了内部复杂性。
std::unique_ptr 的实现要简单得多,并且当您知道处理程序不能被中断时可以很好地工作。请注意,客户端代码只能看到原始指针 - 它不能窃取 unique_ptr!
我希望标题没有误导,但我在一个问题中找不到合适的语言来定义我的问题。
最近我一直在尝试使用 C++ 实现 "CQRS Command Handler" 设计模式。我有 2 个层次结构,我必须结合在一起:
ICommand
struct ICommand { virtual ~ICommand() = default; }; struct SampleCommand : ICommand { int sampleParameter; SampleCommand() : sampleParameter(0) { } explicit SampleCommand(const int sampleParameter) { this->sampleParameter = sampleParameter; } };
ICommandHandler
template<typename T, typename = std::enable_if_t<std::is_base_of<ICommand, std::decay_t<T>>::value>> struct ICommandHandler { virtual void Handle(std::shared_ptr<T> command) = 0; virtual ~ICommandHandler() = default; }; class SampleCommandHandler : public ICommandHandler<SampleCommand> { public: void Handle(std::shared_ptr<SampleCommand> command) override { std::cout << "sampleParameter " << command->sampleParameter << std::endl; } };
我需要实现的最后一部分是一个调度程序,它接受一个命令,找到一个处理程序并将命令委托给找到的处理程序。我想到的第一个想法是公开一些处理程序在调度程序中注册 API 并编写调度方法,该方法将简单地尝试 dynamic_cast 所有已注册的处理程序,如果某些转换成功,它将调用找到的处理程序作为如下所示:
class Dispatcher
{
public:
template<typename T>
void Dispatch(std::shared_ptr<T> command)
{
auto handler = std::find_if(std::begin(_handlers), std::end(_handlers), [](auto handler)
{
return dynamic_cast<ICommandHandler<T>*>(handler);
});
if(handler != std::end(_handlers))
{
(*handler)->Handle(command);
}
}
private:
std::vector<?> _handlers;
};
问题是:“_handlers”std::vector 应该存储什么类型才能使 Dispatcher::Dispatch 方法正常工作,如果这可能的话?
到目前为止我尝试过的:
std::vector< ICommandHandler *> - 未编译,因为无法将具体处理程序强制转换为 ICommandHandler< ICommand> *>。
Error C2440 'initializing': cannot convert from 'SampleCommandHandler *' to 'ICommandHandler<ICommand,void> *'
std::vector
- 未编译,因为 dynamic_cast 无法应用于 void*
你在这里有一堆毫无意义的(至少对我来说)多态性;我会根据它们处理的内容拆分处理程序的容器,而不是使用一个向量。就像从类型索引到处理程序的 map/unordered 映射;或者,有一组固定的类型来处理。
但是要解决问题问:
struct ICommandHandlerBase{
virtual ~ICommandHandlerBase(){};
};
template<typename T, typename = std::enable_if_t<std::is_base_of<ICommand, std::decay_t<T>>::value>>
struct ICommandHandler:ICommandHandlerBase
{
现在存储 ICommandHandlerBase*
或 unique_ptr<ICommandHandlerBase>
的向量。
当然是用map或者无序map来查。如果必须,您可以使用带有自定义比较器的(无序)集合。虽然那不能回答你的问题:-)
假设您的处理程序确实是多态的,那么您的映射或集合必须是指向接口 ICommandHandler 的指针。它们不应该是原始指针,而可以是某种智能指针。
使用 std::shared_ptr 意味着处理程序对象的生命周期在处理事件时得到保证 - 如果它也需要 shared_ptr,即使事件从映射中删除(在处理程序内部或由另一个线程或处理程序),但增加了内部复杂性。
std::unique_ptr 的实现要简单得多,并且当您知道处理程序不能被中断时可以很好地工作。请注意,客户端代码只能看到原始指针 - 它不能窃取 unique_ptr!