c++ 在基类上自动推导派生类上函数的返回类型

vktxenjb  于 2023-02-06  发布在  其他
关注(0)|答案(2)|浏览(158)

我想在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类,它看起来就不那么漂亮了

x8diyxa7

x8diyxa71#

这在C中基本上是不可能的。C不以这种方式工作,原因很简单。让我们假设这种方式在某种程度上是可行的。考虑下面的简单函数:

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

现在问问你自己:value的类型是什么?2你可能不知道,但是在C中没有这样的类型auto。3 auto是C编译器***的占位符,用于推导,或者在编译时确定实际的类型***。4 auto基本上是说:不管表达式的类型是什么,它就是这个对象的类型。如果你的C编译器确定p->value()返回一个int,那么value就是一个int,上面的语句100%等价于声明int value=p->value();
在这里,不可能确定value的实际类型。它是int?还是double?还是其他类型?
不幸的是,这是一个永远无法解开的谜团,type的实际值取决于Base指针实际指向的派生对象,这在编译时是***未知的***,只能在运行时*确定
C
的一个基本属性是,***所有***对象的类型*必须在编译时推导出来。这是C固有的。没有变通方法。没有替代方法。您尝试做的事情在C中无法完成。
不过,也有一点好消息:如果可能的返回类型数量有限,只需声明一个普通的虚方法returns a std::variant,每个派生类就可以返回一个合适的值,调用者可以使用它。
在上述情况下,这将是:

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

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

4xrmg8kj

4xrmg8kj2#

抽象基类通常用作实现类的公共接口。在这种情况下,接口随每个子类而变化:当返回类型改变时,函数签名也改变,这就是为什么override会导致编译错误,正如你可能已经意识到的那样。
Visitor非常有用,前提是类系统稳定,如here所述:
1.请确认当前层次结构(称为元素层次结构)相当稳定,并且这些类的公共接口足以满足访问者类所需的访问权限。如果不满足这些条件,则访问者模式不匹配。
要实现Visitor,通常需要定义多个具有不同输入参数类型的函数,而不是使用动态强制转换(如上面链接中所述)。
你也可以完全取消类继承。看看Sean Parent的talk。他描述了一个类似的用例,并使用模板来做你可能想做的事情。诀窍是定义一个类,它带有一个模板化的构造函数和一个on对象类型来与构造函数一起使用。

相关问题