2

我想在 c++14 上实现类似的东西,基本上派生类可以有不同类型的返回类型(例如 int、double、string 等)

class Base {
 public:
   virtual auto value() = 0; // I know this won't compile
};

class Derived1 : public Base {
 public:
   double value() override {
     return 1.0;
   };
};                                                                                                                      
class Derived2 : public Base {
 public:
   int value() override {
     return 1;
   };
};

我知道上面的代码不会编译,但我正在尝试使用任何可能的方式或模式来实现类似的东西(我尝试过模板、CRTP、访问者,但没有什么能满足我的以下代码)

Derived1 d1;
Derived2 d2;
std::vector<Base*> base = {&d1, &d2);
for (const auto* b : base) {
  std::cout << b->value();
}

我能用模板得到的最好的东西是

Derived1 d1;
Derived2 d2;
std::vector<Base*> base = {&d1, &d2);
for (const auto* b : base) {
  if (dynamic_cast<Derived1>(b)) {
    std::cout << b->value<double>();
  } else if (dynamic_cast<Derived2>(b)) {
    std::cout << b->value<int>();
  }
}

但是如果我有 100 种派生类,它看起来就不会那么漂亮了:D

4

2 回答 2

3

从根本上说,这在 C++ 中是不可能的。C++ 不能以这种方式工作,原因如下。让我们假设这以某种方式起作用。考虑以下简单函数:

void my_function(Base *p)
{
    auto value=p->value();
}

现在,问问自己:value这里的类型是什么?您可能没有意识到这一点,但auto在 C++ 中没有调用这种实际类型。auto是 C++ 编译器在编译时推断或确定实际类型的占位符。auto基本上说:无论表达式的类型计算结果是什么,这就是这个对象的类型。如果您的 C++ 编译器确定p->value()返回一个int, thenvalue是一个 int,并且上面 100% 等同于声明int value=p->value();.

在这里,无法确定values 的实际类型。是int吗?是double吗?或者是其他东西?

不幸的是,这是一个永远无法解开的谜团。的实际值type取决于指针实际指向的派生对象,在编译时Base是未知的,只能在运行时确定。

必须在编译时推导出所有对象的类型,这是 C++ 的一个基本属性。这是烘焙到 C++ 中的。没有解决方法。没有其他选择。您尝试做的事情无法在 C++ 中完成。

但是,有一点好消息:如果可能的返回类型数量有限,只需声明一个返回 astd::variant的普通虚方法,然后每个派生类都可以返回适当的值。由调用者来使用它。

在上述情况下,这将是:

class Base {
 public:
   virtual std::variant<int, double> value() = 0;
};

如果返回的实际值的类型完全未知,那么我想您可以使用std::any. 在任何一种情况下,当您尝试实现任何一种方法时,您都会发现 C++ 会强制您找出并检查每种可能的类型(方式取决于您std::variant是否std::any使用这种方法。

于 2021-05-30T13:36:16.277 回答
0

抽象基类通常用作实现类的公共接口。在这种情况下,您的接口会随着每​​个孩子而改变:当返回类型改变时,函数签名也会改变,这就是为什么override会导致编译错误,正如您可能已经意识到的那样。

如果您的班级系统如此稳定,那么访客很有用:

  1. 确认当前层次结构(称为元素层次结构)将相当稳定,并且这些类的公共接口足以满足访问者类所需的访问权限。如果不满足这些条件,则访问者模式不是很好的匹配。

要实现访问者,您通常会定义具有不同输入参数类型的多个函数,而不是使用动态转换(如上面的链接所述)。

您也可以完全取消类继承。查看 Sean Parent 的演讲。他描述了一个类似的用例,并使用模板来做你可能想做的事情。诀窍是定义一个具有模板化构造函数的类和与构造函数一起使用的对象类型。

于 2021-05-30T14:00:32.900 回答