在基 class 上自动推导 return 派生 class 上的函数类型

Deduce return type of a function on derived class automatically on base class

我想在 c++14 上实现类似的东西,基本上派生的 class 可以有不同类型的 return 类型(例如 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 种 Derived class,它看起来就不会那么漂亮了:D

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

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

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

在这里,无法确定value的实际类型。是 int 吗?是 double 吗?还是别的?

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

属性 所有对象的类型[=52]是C++的基础属性 =]必须在编译时推导。这是烘焙到 C++ 中的。没有解决方法。别无选择。你正在尝试做的事情无法在 C++ 中完成。

但是,有一点好消息:如果可能的 return 类型数量有限,只需声明一个 returns a std::variant 的普通虚方法,并且每个派生 class然后可以return一个合适的值。由调用者使用它。

在上述情况下,这将是:

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

如果被 returned 的实际值的类型完全未知,那么我想你可以使用 std::any。在任何一种情况下,当您尝试实现任何一种方法时,您会发现 C++ 将 强制 您找出并检查每种可能的类型(在取决于您是使用 std::variant 还是 std::any) 的方法,每次您尝试使用从该方法 return 得到的值时。

抽象基 class 通常用作实现 class 的 public 接口。 在这种情况下,您的界面会随着每个 child 发生变化:当 return 类型发生变化时,函数签名也会发生变化,这就是为什么 override 会导致编译错误,您可能已经意识到这一点。

如果您的 class 系统如描述的那样稳定 here:

,那么访问者很有用
  1. Confirm that the current hierarchy (known as the Element hierarchy) will be fairly stable and that the public interface of these classes is sufficient for the access the Visitor classes will require. If these conditions are not met, then the Visitor pattern is not a good match.

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

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