【问题标题】:C++ workaround for templated polymorphism模板化多态性的 C++ 解决方法
【发布时间】:2021-08-05 18:28:35
【问题描述】:
class Base{
public:
    template <typename T> T getValueAs()
    {
        //should return (T)this->getValue()
    }
    template <typename T> void setValueFrom(T val)
    {
        //should call this->setValue((Impl::value_type)val)
    }   
};

template<typename T> class Impl : public Base {
public:
    virtual void setValue(const T& val){ ... }
    virtual T getValue() const{ ... }
protected:
    T value;
};

//also some very speficic impl would be
class NumVal : public Base<int>{
};

我想把它用作:

Impl<float> var;
var.setValue(42.0f);
Base* baseVar = (Base*)&var;

int convertedVal = baseVar->getValueAs<int>(); // should return (int)(42.0f)

我知道我们不能覆盖模板成员函数,这里的限制是Base类不知道Impl类的确切值类型,有没有替代方法来实现类似的东西?

否则,我需要在基类上实现非常具体的转换,以便在后代类上覆盖:

class Base{
public:
   virtual std::string getString() = 0;
   virtual int getInt() = 0;
   virtual bool getBool() = 0;
};

所以我需要的只是Base::get&lt;T&gt;()

【问题讨论】:

  • 为什么Base不是带有非模板方法的模板?
  • 在 CRTP 模式中,您的 Base 将是一个模板,而 Impl 将是 template&lt;typename T&gt; class Impl : public Base&lt;Impl&lt;T&gt;&gt;。如果您这样做,您将能够在Bases getValue() 中投降。虽然不清楚为什么要这样做,因为getValue 将始终在派生类中被覆盖。
  • 问的不同:为什么首先使用继承?假设您可以覆盖模板方法,您将无法多态地使用它们,因为它们具有不同的参数/返回类型
  • Base 不能是模板类,因为我需要在“用户”不知道 var 所持有的确切类型的情况下传递它,我只是希望它像 void*
  • 是否可以从 Impl&lt;Bar&gt;getValueAs&lt;Foo&gt; ?编辑:是的,这实际上是示例显示的内容。

标签: c++ templates reflection type-conversion polymorphism


【解决方案1】:

如果你坚持设计,可以在std::any的一点帮助下实现。请注意,以下解决方案无法满足此要求:

int convertVal = baseVar->getValueAs(); // 应该返回 (int)(42.0f)`

目前我没有看到实现它的方法。

class Base {
public:
    template <typename T> T getValueAs() const
    {
        return std::any_cast<T>(getValueImpl());    
    }
    
    virtual std::any getValueImpl() const = 0;
}

template<typename T> class Impl : public Base {
public:
   std::any getValueImpl() const override { return std::make_any(value); }
protected:
    T value;
}

这在运行时是类型安全的 - 即如果类型不匹配将引发异常。缺点是您将付出双重类型擦除的代价(一个在您的Base 中,另一个在std::any 操作中)。

【讨论】:

  • 使用dynamic_cast&lt;Impl&lt;T&gt;&gt;(this),只有一种类型的擦除。
  • @Jarod42 OP 从未说过 Impl&lt;T&gt; 是唯一可能的基地后代。在父类方法中dynamic_cast也有点别扭。
  • 我尝试了这个解决方案,但它并没有解决我的问题,因为大多数类型不兼容,比如 int 到 string,这就是为什么我需要虚拟覆盖才能工作
  • @uray 实际上像intstring 这样的例子让我有点困惑。没有什么魔法可以让各种组合都奏效。
  • @uray 在你的例子中你没有intstring 的转换。这在当前的设计中是做不到的。当然,还有其他一些稍微不同的设计可以适应它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
  • 2015-02-18
  • 2013-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多