派生模板覆盖成员函数C ++的返回类型

时间:2010-05-26 16:52:45

标签: c++ templates polymorphism generic-programming template-specialization

我正在编写矩阵类。看看这个定义:

template <typename T, unsigned int dimension_x, unsigned int dimension_y> 
class generic_matrix
{
  ...
  generic_matrix<T, dimension_x - 1, dimension_y - 1> 
  minor(unsigned int x, unsigned int y) const
  { ... }
  ...
}

template <typename T, unsigned int dimension> 
class generic_square_matrix : public generic_matrix<T, dimension, dimension>
{
  ...
  generic_square_matrix(const generic_matrix<T, dimension, dimension>& other)
  { ... }
  ...
  void foo();
}

generic_square_matrix类提供了矩阵乘法等附加功能。 这样做没有问题:

generic_square_matrix<T, 4> m = generic_matrix<T, 4, 4>();

由于构造函数,可以将任何方阵矩阵分配给M,即使类型不是generic_square_matrix。这是可能的,因为数据不会在子节点之间发生变化,只会受支持的功能。这也是可能的:

generic_square_matrix<T, 4> m = generic_square_matrix<T, 5>().minor(1,1);

此处适用相同的转换。但现在出现了问题:

generic_square_matrix<T, 4>().minor(1,1).foo(); //problem, foo is not in generic_matrix<T, 3, 3>

要解决此问题,我希望generic_square_matrix :: minor返回generic_square_matrix而不是generic_matrix。我认为唯一可行的方法是使用模板专业化。但由于专业化基本上被视为一个单独的类,我必须重新定义所有函数。我不能像使用派生类那样调用非专用类的函数,所以我必须复制整个函数。 这不是一个非常好的通用编程解决方案,还有很多工作。

C ++几乎有一个解决我的问题的方法:派生类的虚函数,如果此类派生自基类返回的类,则可以返回指向基类返回的不同类的指针或引用。 generic_square_matrix 派生自generic_matrix,但该函数不返回指针或引用,因此这里不适用。

是否有解决此问题的方法(可能涉及完全其他结构;我唯一的要求是尺寸是模板参数,方形矩阵可以有其他功能)。

提前致谢,

路德

4 个答案:

答案 0 :(得分:4)

您可以在派生类中实现该功能而不使其成为虚拟。这将“隐藏”基类实现,并且可能适合您的使用,尽管一般厌恶隐藏成员函数。

更好的方法可能是使用不同的名称,尽管这可能会破坏界面的“纯度”。

最后 - minor()可以实现为自由函数而不是成员函数吗?然后,您可以根据需要提供重载,并在编译时调用正确的函数。这是我打开的方法隐藏案例最接近的,但我怀疑它会被普遍认为是“良好做法”。

答案 1 :(得分:3)

如果您可以简单地使minor()成为自由函数,则无需使设计复杂化:

template<typename T, unsigned int X, unsigned int Y> 
generic_matrix<T, X-1, Y-1> 
minor(const generic_matrix<T, X, Y>& m, unsigned int x, unsigned int y);

...然后您可以根据需要添加替代版本。

答案 2 :(得分:1)

如果不以多态方式使用这些类型,则可以非虚拟地覆盖派生类中的基类方法,以便返回派生类对象。它会隐藏基类版本,这通常是不需要的,但在你的情况下它似乎是。

或者,您可以使这些成员函数使用自由函数模板,如果需要,可以使用不同类型矩阵的重载。 (虽然模板化可能会自动解决这个问题。)

答案 3 :(得分:0)

模板专业化将起作用。你所做的是获取共享代码并将其放在基类中,然后从两者继承。保护基地中的析构函数,这样任何人都无法通过投射到基础并试图删除它来破坏你的概念。

像这样:

namespace detail_
{
  template < typename T >
  struct my_templates_common_functionality
  {
    void f1() {}
    void f2() {}
  protected:
    ~my_templates_common_functionality() {}
  };
}

template < typename T >
struct my_template : detail_::my_templates_common_functionality<T>
{
  int f3() { return 5 }
};

template < >
struct my_template<double> : detail_::my_templates_common_functionality<double>
{
  char* f3() { return nullptr; }
};

还有其他方法,但这是最简单的方法之一。