首页 > 解决方案 > 抽象方法的 C++ 模板返回类型(类似于 java)

问题描述

假设我有一个 Java 访问者。

interface Visitor<T> {
    T visitA(VisitableA a);
    T visitB(VisitableB b);
}

abstract class Visitable {
    abstract <T> T accept(Visitor<T> visitor);
}

class VisitableA extends Visitable {
    @Override <T> T accept(Visitor<T> visitor) {
        return visitor.visitA(this);
    }
}

class VisitableB extends Visitable {
    @Override <T> T accept(Visitor<T> visitor) {
        return visitor.visitB(this);
    }
}

现在,如果我想用 C++ 编写它,我可以这样做

class VisitableA;
class VisitableB;

template <typename T> class Visitor {
    virtual T visitA(VisitableA& a) = 0;
    virtual T visitB(VisitableB& b) = 0;
};

class MyVisitor : public Visitor<int> {
    int visitA(VisitableA& a) override { /* do something */ return 42; }
    int visitB(VisitableB& b) override { /* do something */ return 1337; }
};

class Visitable {
    template <typename T> virtual T accept(Visitor<T>& visitor) = 0;
};

class VisitableA : public Visitable { 
    template <typename T> T accept(Visitor<T>& visitor) override { return visitor.visitA(*this); }
};

class VisitableB : public Visitable { 
    template <typename T> T accept(Visitor<T>& visitor) override { return visitor.visitB(*this); }
};

但这不会编译,因为我不能有一个虚拟模板方法。

我可以使用std::any(或类似的东西),但我想知道是否有办法在没有它的情况下实现它。

必须注意的accept是,在构造 s 时返回类型是未知Visitable的。

如果需要,C++20 也可以。

标签: c++templates

解决方案


做 Java 在底层所做的事情。

替换Tstd::any

用于Visitable创建返回any两种方法的基类。any然后创建一个实现返回 as的模板类final,并创建一个T返回Visitas pure 供实现者实现。

Visitable有两种方法;一个采用 的纯虚拟私有模板,一个采用模板Visitable的公共VitiableT模板。它调用纯虚拟的并将 转换any为 a T

Java 泛型基本上只是为您编写粘合剂。

您可能需要在引擎盖下进行强制转换,或者查看 typeids,这取决于您如何使用它。

struct VistableA; struct VistableB;
struct Visitor{
  virtual std::any visitA(VisitableA&)=0;
  virtual std::any visitB(VisitableB&)=0;
};
template<class T>
struct VisitorT:Visitor{
  std::any visitA(VisitableA& a) final{ return visitTA(a); }
  T visitTA(VisitableA& a) = 0;
  std::any visitB(VisitableB& b) final{ return visitTB(b); }
  T visitTB(VisitableB& b) = 0;
};

class MyVisitor : public VisitorT<int> {
  int visitTA(VisitableA& a) override { /* do something */ return 42; }
  int visitTB(VisitableB& b) override { /* do something */ return 1337; }
};

class Visitable {
  template <typename T>  T accept(VisitorT<T>& visitor) {
    return std::any_cast<T>(accept(static_cast<Visitor&>(visitor)));
  }
  virtual std::any accept(Visitor& visitor)=0;
};

class VisitableA : public Visitable { 
  std::any accept(Visitor& visitor) override { return visitor.visitA(*this); }
};

class VisitableB : public Visitable { 
  std::any accept(Visitor& visitor) override { return visitor.visitB(*this); }
};

现在这是 C++,还有另外六种方法可以解决您的问题,它们不模拟 Java。但这是 Java 式的方式。

请注意,因为我们正在编写 Java 为您编写的胶水代码,如果我们在胶水中出错,我们就不是类型安全的。

并且在VisitableAJava 中检查所有Ts 是否使用相同的“通用类型”;在 C++ 中,我使用了 a std::any,它可能与其他“泛型”混合并破坏了某些类型安全。你可以想象一个复杂的标记系统std::any来避免这种情况。


推荐阅读