在基类上自动推断派生类上函数的返回类型

Posted

技术标签:

【中文标题】在基类上自动推断派生类上函数的返回类型【英文标题】:Deduce return type of a function on derived class automatically on base class 【发布时间】:2021-05-30 13:25:33 【问题描述】:

我想在 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 种类型的 Derived 类,它看起来不会那么漂亮:D

【问题讨论】:

看起来你想要在 C++14 中使用 C++17 的 std::any 是的,std::any 可以工作,但我的开发套件中没有 c++ 17,谢谢 您可以考虑 Boost 的 any,您可以将其用于 C++14。 【参考方案1】:

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

void my_function(Base *p)

    auto value=p->value();

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

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

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

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

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

在上述情况下,这将是:

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

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

【讨论】:

【参考方案2】:

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

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

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

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

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

【讨论】:

其他答案建议使用 std::any 或 std::variant。在我看来, std::any 在这里是一个不好的方法,因为它不允许您强制执行接口要求。 std::variant 需要了解所有可能的派生类型并且不可扩展。 谢谢,我实际上也使用了访问者模式来解决这个问题,但最后我仍然需要知道每个派生类的类型是什么......正如 Sean Parent 所说,它真的不能解决我的问题,因为它仍然无法返回基于派生类型的值,在他的谈话中,他使用了不返回任何值的“void draw” 啊。我错误地认为基于派生类型的返回类型是使用公共接口或公共集合返回不同类型结果的要求。如果返回类型是您的主要要求,那么不,它不会解决这个问题。

以上是关于在基类上自动推断派生类上函数的返回类型的主要内容,如果未能解决你的问题,请参考以下文章

Effective C++ 6.继承与面向对象设计

使用纯多态性和继承从基类调用派生类上的函数而不进行强制转换?

类的继承与派生

如何在派生类上动态调用静态方法

派生类上的C ++赋值运算符实现[重复]

为啥在基类构造函数中看不到派生类属性值?