2009-08-09 62 views
3

我有一个矩阵类,其大小由模板参数决定。如何避免C++类模板中的无限递归

template <unsigned cRows, unsigned cCols> 
class Matrix { 
    ... 
}; 

我的程序使用几个大小的矩阵,通常是2x2,3x3和4x4。通过使用模板参数而不是运行时参数设置矩阵大小,编译器可以进行大量内联和优化。

但现在我需要一个成员函数,它返回一个新的矩阵,它有一个较少的行和一个较少的列。

Matrix<cRows - 1, cCols - 1> Reduced(unsigned row, unsigned col) const { ... } 

这个想法是,它会返回一个矩阵,删除指定的行和列。实际上,这只会被称为矩阵,它至少有三行三列,最小返回2x2。

编译器没有看到下限,所以它陷入了无限递归中,试图实例化具有不断减小的大小的模板。我试图把在函数本身两条线索,这些更小的尺寸就不会发生:

Matrix<cRows - 1, cCols - 1> Reduced(unsigned row, unsigned col) const { 
    static_assert(cRows > 1 && cCols > 1); 
    if (cRows <= 1 || cCols <= 1) throw std::domain_error(); 
    Matrix<cRows - 1, cCols - 1> r; 
    // ... initialize r ... 
    return r; 
} 

无论是static_assert还是if语句来似乎是一个足够强大的线索,一个为0x0矩阵将永远不会被编译器生成。 (具有讽刺意味的是,它确实抱怨if -statement具有恒定的编译时间条件。)

有没有人有关于如何避免此编译时无限递归的任何建议?

回答

12

您需要为没有行或无列的Matrix提供专业化。

E.g.

template<unsigned cRows> 
class Matrix< cRows, 0 > 
{ 
    Matrix<cRows - 1, 0> Reduced() { return Matrix<cRows - 1, 0>(); } 
}; 


template<unsigned cCols> 
class Matrix< 0, cCols > 
{ 
    Matrix<0, cCols - 1> Reduced() { return Matrix<0, cCols - 1>(); } 
}; 


template<> 
class Matrix< 0, 0 > 
{ 
    Matrix<0, 0> Reduced() { return Matrix<0, 0>(); } 
}; 

你的问题是,试图与一组特定的模板参数来实例化矩阵精简功能总是需要实例化矩阵模板一组不同的参数(乌鸦 - 1,cCols -1)。这个递归必须在某个地方停止。如果你只处理矩形矩阵,那么你就可以用更少的专门技术逃脱。

此外,如果您永远不会使用1x1矩阵(2x2矩阵上的缩减结果),则可以使用完全空的类来停止递归。

template<> 
class Matrix< 1, 1 > {}; 
+0

他说2x2是最小的,但我仍然认为这是最好的。也许可以添加'const int MinimumRows','const int MinimumColumns',这样它就可以调整。 – GManNickG 2009-08-09 20:09:26

+0

谢谢,这个伎俩。我更进了一步,使'Reduced'成为非成员函数,使得专业化更容易。 – 2009-08-09 21:00:05

0

您需要明确指定您希望递归结束的情况下的行为。有关更多详细信息,请参阅this DDJ article。下面是文章一个简单的例子:

template<int n> 
class META_FACTORIAL 
{ 
public: 
    enum{ 
    RET = n * META_FACTORIAL<n-1>::RET 
    }; 
}; 

template<> 
class META_FACTORIAL<0> 
{ 
public: 
    enum{ RET = 1 }; 
}; 
1

您可以指定不包括该方法乌鸦或cCols的小值的模板特。

1

您对编译时间和运行时行为似乎有点困惑,而且我对代码有些困惑,但我认为您需要的是模板的值为0,0的特殊值,它终止递归。

如果你还没有得到它,我建议你阅读 C++ Templates: The Complete Guide范德沃尔德& Josuttis,其中涵盖了这类事情的细节。

0

不是专业全班终止递归,另一个选择可能是使用boost::enable_if的功能,使其仅当矩阵大小是2×2以上。