我在这里尝试实现的案例是一个基类,它有一个函数(我们称之为 modify_command),它实际上可以接受许多不同的类型,因此派生类可以实现它们认为合适的 modify_command 函数。 现在我在基类中有一些类似的东西:
class Base
{
template<typename Command>
void modify_command(Command cmd)
{
std::cout << "Modify command called with unimplemented command type:" << typeid(cmd).name();
}
virtual void modify_command(SpecificCommandA cmd)
{
modify_command<SpecificCommandA>(cmd); // Calls the templated function
}
virtual void modify_command(SpecificCommandB cmd)
{
modify_command<SpecificCommandB>(cmd); // Calls the templated function
}
// etc.
};
然后在派生类中:
class Derived : public Base
{
virtual void modify_command(SpecificCommandA cmd)
{
cmd.x = 1;
cmd.y = 2;
}
}
显然,虚拟模板函数是不可能的,所以在某种形式下,我将不得不列出无数参数可能性的函数声明,这肯定会使类定义困惑,并且可能使(随着时间的推移)难以额外要处理的命令类型
具有模板化函数的目的是为了在这种情况下编译而不需要定义 modify_command(SpecificCommandC) 来记录错误:
Base * base = new Derived();
SpecificCommandA a;
SpecificCommandB b;
SpecificCommandC c;
base->modify_command(a); //Set's x and y
base->modify_command(b); //Outputs that command type is unimplemented
base->modify_command(c); //Outputs that command type is unimplemented
我真的很讨厌我的工作方式,有人对如何清理/重新实现它有什么建议吗?随着软件的成熟,命令的数量将继续增长,因此可扩展性是必须的。
编辑:语法
最佳答案
不幸的是,虚拟模板方法无法解决您的问题。
这里有一个更 C-ish 的解决方案,可以解决这个限制:
#include<unordered_map>
#include<functional>
#include<memory>
#include<iostream>
#include<utility>
struct BaseCommand {
static int counter;
};
int BaseCommand::counter = 0;
template<class T>
struct Command: BaseCommand {
static int type() {
static const int t = ++counter;
return t;
}
};
struct SpecificCommand1: Command<SpecificCommand1> {};
struct SpecificCommand2: Command<SpecificCommand2> {};
class Base {
struct Handler {
virtual void operator()(BaseCommand &cmd) = 0;
};
template<typename T>
struct THandler: Handler {
std::function<void(T)> func;
void operator()(BaseCommand &cmd) override {
func(static_cast<T&>(cmd));
}
};
protected:
template<typename T>
void assign(std::function<void(T)> f) {
auto handler = std::make_unique<THandler<T>>();
handler->func = f;
handlers[T::type()] = std::move(handler);
}
public:
template<typename Command>
void modifyCommand(Command cmd) {
auto it = handlers.find(Command::type());
if(it == handlers.end()) {
std::cout << "Modify command called with unimplemented command type: " << Command::type();
} else {
auto &h = *(it->second);
h(cmd);
}
}
private:
std::unordered_map<int, std::unique_ptr<Handler>> handlers;
};
class Derived: public Base {
public:
Derived() {
std::function<void(SpecificCommand1)> f =
[](SpecificCommand1) {
std::cout << "handler for SpecificCommand1" << std::endl;
};
assign(f);
}
};
int main() {
Base *b = new Derived;
b->modifyCommand(SpecificCommand1{});
b->modifyCommand(SpecificCommand2{});
}
基本思想是在运行时为您的命令提供数字类型(这可以使用 CRTP 习惯用法来完成 - 请参阅 BaseCommand
和 Command
模板类)。
有了这个值,就可以创建一个type-erased 处理程序来处理您要为其提供特定实现的命令(参见 assign
和 处理程序
/THandler
).
一旦您正确设置了所有部分,您只需在派生类中设计和初始化这些处理程序。因为它可以使用 std::function
来完成,您可以使用 lambda、公共(public)或私有(private)成员方法、静态方法等作为处理程序。
有关详细信息,请参阅 Derived
的构造函数。
关于C++ 设计 : Overloading/Overriding many many functions, 的清理方式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37757124/