【问题标题】:Convert from base to derived class at runtime using template logic使用模板逻辑在运行时从基类转换为派生类
【发布时间】:2020-09-22 05:31:51
【问题描述】:

我正在编写一个小型数据库,似乎遇到了问题。我有 3 个Statement 类型(每个类型对应CREATEINSERTSELECT。这些类型的定义是:

class Statement { }; // Base class, contains common stuff like the table name

class CreateStatement : public Statement {
  /* Contains information regarding the CREATE statement after parsing (name and datatype of
  each column) */
}

class InsertStatement : public Statement {
  /* Contains information regarding the INSERT statement (list of values to enter inside the DB) */
}

class SelectStatement : public Statement {
  /* Contains information regarding the SELECT statement ( list of attributes to select from the DB) */
}

我的解析器具有适当解析这 3 种类型的输入语句的函数,但我从这些函数中的每一个返回基类。一个例子是:

Statement parse_create(const std::string& stmt) {
  CreateStatement response;
  // Parse stmt
  return response;
}

我这样做的理由是避免在我的主 REPL 循环中出现 if/else 情况(而不是检查输入是否为 create 类型,然后有条件地返回 CreateStatement 或其他派生类,只需调用一次 parse 并这将返回一个Statement 对象)。我遇到的问题是执行返回的Statement。我有 3 个不同的函数来执行这些命令(execute_createexecute_insertexecute_select),它们分别将派生类作为参数(CreateStatementInsertStatementSelectStatement)。这样执行函数就可以在解析时利用存储在派生类对象中的信息。

我是否可以使用某种模板逻辑来调用适当的执行函数,或者在解析后返回基类后是否会丢失派生类的信息?我尝试做一些类似的事情:

Statement parsed_stmt = parse(input); // input is the string the user entered
if (std::is_same_v<decltype(input), CreateStatement>) {
  execute_create(parsed_stmt);
}

但这显然行不通,因为类型是明确的Statement

如果您对设计提供任何反馈,我也将不胜感激。谢谢!

【问题讨论】:

  • 当您按值返回时,您 slice 对象。多态性仅适用于引用或指针。当然,这假设您的类层次结构是多态的。如果不是,那么在没有大量向上和向下转换的情况下,对基类的指针或引用将无法工作。
  • 这类事情的首选工具是虚拟函数,而不是模板。你试过了吗?
  • 您不能按值返回基本类型,它会分割您的对象并删除任何派生功能。欢迎使用 C++,由于糟糕的设计,功能相互争斗。

标签: c++ oop c++14 c++17


【解决方案1】:

我会这样做的方式是这样的:

class Statement {
    ...
    virtual void parse(const std::string &stmt) = 0; // A string view will work here too.
    virtual bool execute() = 0;
    ...
};

class Create : public Statement {
    ...
    virtual void parse(const std::string &stmt); // Implement
    virtual bool execute(); // Implement
    ...
};

... // more statements

然后在派生类中适当地实现这些。您可以解析非虚拟并调用虚拟 create 方法,但这对我来说似乎是多余的。注释创建不返回任何内容 - 它只是更改对象的状态(存储语句或其他内容)。

同样,execute 存储在派生类中,因此它知道如何执行自己 - 即使您引用了 Statement 对象,也会毫不费力地调用正确的派生方法。我假设返回一个布尔值表示成功。

这意味着您需要使用对语句的引用(或指针)。这是多态性继承的主要优势——能够以相同的方式对不同的派生对象进行操作,而不必担心它们究竟是什么。

【讨论】:

  • 非常感谢,非常感谢。这解决了我的问题。继续做更多的项目和学习语言!
【解决方案2】:

动态调度:

struct Base {
  abstract virtual void method() = 0;
  virtual ~Base() = default;
};

struct Derived: Base {
  void method() override {}
};

静态调度 (CRTP),通常是矫枉过正:

template <class D> struct Base {
  void method() {
    static_cast<D*>(this)->method();
  }
};

struct Derived: Base<Derived> {
  void method() { ... }
};

【讨论】:

    猜你喜欢
    • 2017-01-13
    • 2021-04-12
    • 1970-01-01
    • 1970-01-01
    • 2012-03-22
    • 2012-06-21
    • 1970-01-01
    • 2019-11-09
    • 1970-01-01
    相关资源
    最近更新 更多