返回专用模板类的协变类型

Posted

技术标签:

【中文标题】返回专用模板类的协变类型【英文标题】:Return covariant type of specialized template class 【发布时间】:2018-04-01 17:07:32 【问题描述】:

我有以下类层次结构:

template<typename T>
class GridMetric
  virtual GridMetric* getNeighbors(T value) = 0;
;

template<size_t N, typename T, typename Derived>
class MatrixBase : public GridMetric<T>
  virtual MatrixBase<N,T,Derived>* getNeighbors(T value)return nullptr;
;

template<size_t N, typename T>
class MatrixND : public MatrixBase<N,T,MatrixND<N,T>>
  virtual MatrixND<2,T>* getNeighbors(T value) /* ... */
;

template<typename T>
class MatrixND<2,T> : public MatrixBase<2,T,MatrixND<2,T>>
  virtual MatrixND<2,T>* getNeighbors(T value) /* ... */
;

template<typename T>
class Vector : public GridMetric<T>
  virtual MatrixND<2,T>* getNeighbors(T value) /* ... */
;

所以我的抽象类GridMetric有两个派生类,Vector和MatrixBase。我的 Matrix 基类具有 crtp 样式派生类 MatrixND,并且有一个 N=2 的 MatrixND 特化。

每个类都应该有一个虚函数 getNeighbors 来返回一个 MatrixND 指针。

一切正常,除了 MatrixND 类抱怨 MatrixND 是无效的协变返回类型:

error: invalid covariant return type for ‘MatrixND<2ul, T>* MatrixND<N, T>::getNeighbors(T&) [with long unsigned int N = 3ul; T = double]’
  virtual MatrixND<2,T>* getNeighbors(T& in)

我的第一个问题是为什么以及如何处理它?由于 MatrixND 继承自 MatrixBase!

我的第二个问题:这是不好的设计,因为我总是会返回原始指针?我读了很多表达式 return new obj..,但也认为这显然是糟糕的设计。是否有其他可能实现相同的目标?

编辑:所以,过了一段时间,我意识到原来的计划行不通,我通过模板类找到了一个更简单的解决方案,我想在其中使用那些类。

无论如何,问题是,为什么专门的 MatrixND 类不能是通用 MatrixND 类中的协变返回类型。我没有找到任何说它是不允许的。

【问题讨论】:

错字? Vector 中的返回类型是MatrixND&lt;2,T&gt;*?并且MatrixND&lt;2,T&gt;中有一个N,应该是2 是的,N 是个错误。是的,计划是返回一个指向二维矩阵的指针。 那么你应该在Vector之前声明MatrixND 你说得对,我把它放在最后,但这对我的代码没有任何影响。我转发声明所有类以排除此类错误。 一般的MatrixND&lt;N, T&gt; 应该返回MatrixND&lt;N, T&gt; *,而不是MatrixND&lt;2, T&gt; * 【参考方案1】: MatrixND&lt;2,T&gt;,基本情况,无需解释。 Vector&lt;T&gt; 继承自 GridMetric&lt;T&gt;,因此从 GridMetric&lt;T&gt; 派生的任何内容(包括此处的 MatrixND&lt;2,T&gt;)都可以。

但是,一般形式 MatrixND&lt;N,T&gt; (N != 2) 继承自 MatrixBase&lt;N,T,MatrixND&lt;N,T&gt;&gt;,它将虚函数重新定义为:

virtual MatrixBase<N,T,Derived>* getNeighbors(T value)return nullptr;

强制MatrixND&lt;N,T&gt; 的返回类型现在(至少)从MatrixBase&lt;N,T,MatrixND&lt;N,T&gt;&gt; 派生。 不是来自GridMetric&lt;T&gt;(对于Vector&lt;T&gt;)或MatrixBase&lt;2,T,MatrixND&lt;2,T&gt;&gt;(对于专业化MatrixND&lt;2,T&gt;),而是:MatrixBase&lt;N,T,MatrixND&lt;N,T&gt;&gt;,带有(N!= 2)。

为什么这不是协变返回类型?因为MatrixND&lt;2,T&gt;N != 2 时不会从MatrixBase&lt;N,T,MatrixND&lt;N,T&gt;&gt; 继承。

要么删除MatrixBase&lt;N,T,MatrixND&lt;N,T&gt;&gt; 中的函数重定义,要么将其返回类型更改为GridMetric&lt;T&gt;,这将起作用(因为MatrixND&lt;2,T&gt; 继承自GridMetric&lt;T&gt;)。

【讨论】:

非常感谢!这完全有道理并且有效。无论出于何种原因,我认为我需要通过层次结构传递函数,如果我不这样做,函数就会再次变为纯虚拟,因此类抽象。我仍然需要研究,对于我的目的来说,更好的解决方案是什么,但这已经非常有帮助了:)

以上是关于返回专用模板类的协变类型的主要内容,如果未能解决你的问题,请参考以下文章

为啥 C# (4.0) 不允许泛型类类型中的协变和逆变?

C#中的协变与逆变

C#中的协变与逆变

c#泛型的协变和逆变

覆盖派生模板类的返回类型

专用模板根据变量类型返回/设置枚举值