【问题标题】:virtual template function workaround虚拟模板功能解决方法
【发布时间】:2012-10-08 06:02:36
【问题描述】:

我偶然发现了一个我(我的任何知识渊博的同事也不知道)知道如何解决(解决)的问题。最终的问题是无法创建虚拟模板功能。我已经彻底搜索了网络,找到了几种处理它的方法,但似乎没有一种适用于我的情况。

我不知道如何简短地描述这种情况,但我会尽力而为,希望它有意义。

问题是处理两条曲线,每条曲线由一个或多个相同或不同曲线类型的段组成。因此,我开始为曲线段创建一个接口,其中包含将与之交互的曲线的模板类(例如,函数与两个不同曲线段之间的交点类型相同):

template<class curve> class curve_segment { // some methods here }

然后,用户可以实现多种类型的曲线,并根据将要与之交互的曲线实现适当的功能。例如,circleline,两者都可以交互:

class line :  public curve_segment<line>, public curve_segment<circle> { //... }
class circle :  public curve_segment<line>, public curve_segment<circle> { //... }

接着,我有一个类cell,它依赖于两个曲线段,以及一个封装它的基类cell_base

template<class curve1, class curve2> class cell : public cell_base { 
  cell_base* up; cell_base* down;

  curve_segment<curve1>* segment_x;
  curve_segment<curve2>* segment_y;

  // some methods that depend on both curves
}

最后,有一个由 m*n 单元组成的二维网格,其中两条曲线由可能不同类型的 m 和 n curve_segments 组成,并由每个单元中的两个指针保持在一起。

问题开始显示何时将新的curve_segment 添加到两条曲线之一。显而易见的解决方案是添加

template<class curve> virtual void add_curve_x(curve_segment<curve> seg) =0;

cell_base 类,cell 中的实现可以提取另一条曲线的适当段,并向其附加一个新单元格。例如,如果曲线ab 其中a 表示网格中的x 轴,我们要在曲线a 上添加另一个曲线段,我们可以找到网格的右侧大多数单元,提取每个此类单元格的 y 轴上的曲线,并从中和新提供的段创建一个新单元格,该单元格将附加到它的右侧。

类型擦除不起作用,因为我们需要知道在创建 cell 时新添加的段的类型(我们需要将两个段的类型作为模板参数提供给 cell 类)。

将模板移动到cell_base 类也不起作用,因为这意味着在每次分配cell 时我必须知道下一个curve_segment 的类型。

有没有办法解决这个问题?

编辑: 按照建议,添加add_curve_x 方法:

template<class curve1, class curve2>
template<class curve> 
cell<curve1, curve>* cell<curve1, curve2>::add_curve_x(curve_segment<curve1>* seg) {
  return new cell<curve1, curve>(seg, (curve_segment<curve>*)(this->segment_y));
}

在这种情况下,seg 必须是 curve 类型,它必须实现 curve_segment&lt;curve1&gt;segment_y 也必须实现 curve_segment&lt;curve&gt;

编辑 2:解释为什么演员在那里

pic

以非粗体矩形为例。在这种情况下,有两条曲线,曲线 a 有 2 段,曲线 b 有 3 段。类line 必须实现curve_segment&lt;line&gt;curve_segment&lt;circle&gt;,并且circle 也必须实现两者。现在让我们向曲线a 添加另一个贝塞尔曲线段。类bezier 必须实现curve_segment&lt;line&gt;curve_segment&lt;circle&gt;,而linecircle 必须实现curve_segment&lt;bezier&gt;

让我们看看我们如何创建三个粗体 cells 的中间部分。我们会打个电话

add_curve_x<bezier>(new bezier(...));

cell&lt;circle, circle&gt;类型的单元格对象上。

参数seg 将是一个新的bezier 对象(可以转换为curve_segment&lt;circle&gt;),这也是curve 类型,segment_y 必须转换为@987654369 @,因为这是它在新创建的单元格中与之交互的曲线类型。

【问题讨论】:

  • 不清楚您要做什么。请在cell 中展示add_curve_x 的具体实现(想象一下允许模板虚函数)。
  • 仍然不清楚......这个演员在那里做什么?您可以将任何曲线段转换为任何其他曲线段吗?
  • 一条曲线可以实现多个不同参数的curve_segments。实现取决于哪种曲线可能相互影响。在这种情况下,单元格中的曲线与curve1curve2 交互,但是当我们添加不同类型的曲线段(curve)时,segment_y 也必须与那个交互。想象一下,你有一条由两条直线和一个圆组成的曲线,第二条由一条贝塞尔曲线和一条圆和一条直线组成。然后一切都必须能够与一切互动。
  • 最后我总是可以强制所有曲线段为同一类型并且没有这个问题,但这会大大降低有用性。
  • 如果您致电 cell&lt;line, circle&gt;::add_curve_x&lt;bezier&gt;,演员将尝试将 curve_segment&lt;circle&gt;* 转换为 curve_segment&lt;bezier*&gt;。因为这两个类不相关,所以这将作为reinterpret_cast 工作,并产生未定义的行为。

标签: c++ templates virtual-functions


【解决方案1】:

添加curve_segment_base:

class curve_segment_base {
   // for double dispatching here:
  virtual cell_base* add_curve_x(cell_base* cell) = 0;
};

template<class curve> 
class curve_segment : public virtual curve_segment_base { 
     // some specific methods here 
     // for double dispatching here:
     virtual cell_base* add_curve_x(cell_base* cell)
     {
         // this has to be moved to cpp file due to dependency from cell_base
         return cell->add_curve_x(this); // now the correct method from cell is called
     }
};

因此,您可以将此方法作为虚拟方法添加到您的 cell_base

class cell_base {
   virtual cell_base* add_curve_x(curve_segment_base* seg) = 0;

   // methods necessary for double dispatching here:
   virtual cell_base* add_curve_x(curve_segment<line>* seg) = 0;
   virtual cell_base* add_curve_x(curve_segment<circle>* seg) = 0;
   ...
};

所以你的方法看起来像,这里有双重调度:

template<class curve1, class curve2>
virtual cell_base* cell<curve1, curve2>::add_curve_x(curve_segment_base* seg) {
  return seg->add_curve_x(this);
}

您将必须为所有细分市场实现所有真正的实现方法:

template<class curve1, class curve2>
virtual cell_base* cell<curve1, curve2>::add_curve_x(curve_segment<line>* seg) {
  return new cell<curve1, line>(...);
}
template<class curve1, class curve2>
virtual cell_base* cell<curve1, curve2>::add_curve_x(curve_segment<circle>* seg) {
  return new cell<curve1, circle>(...);
}

【讨论】:

  • 谢谢!这是一个很好的解决方案,但这是我试图避免的事情,因为用户必须实现add_curve_x,实际上不需要它。我想我应该选择一种更合适的语言:)
猜你喜欢
  • 2019-06-14
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 2014-06-11
  • 2015-01-04
  • 2011-02-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多