模板函数的歧义递归定义

时间:2020-04-23 16:18:49

标签: c++ templates

我正在构建一个静态类型的Matrix,其中所有带有矩阵的操作都经过类型检查。但是,当我想做一些基于给定数字修改矩阵的事情时,我会遇到问题。

例如,添加一列很简单:

template<int A, int B>
Matrix<A,B+1> addOneCol(Matrix<A,B> m1) {
    return Matrix<A,B+1>();
}

但是,添加N列要困难得多。由于不可能使用具有分支的函数进行类型检查,而分支的返回类型不是预期的(即使分支条件可以保证),所以我只能考虑一种递归方法:

template<int A, int B, int Z>
Matrix<A,B+1> addZCols(Matrix<A,B> m1) {
    return addOneCol(m1);
}

template<int A, int B, int Z>
Matrix<A,B+Z> addZCols(Matrix<A,B> m1) {
    return addOneCol(addZCols<A,B,Z-1>(m1));
}

template<int A, int B>
Matrix<A,B+1> addOneCol(Matrix<A,B> m1) {
    return Matrix<A,B+1>();
}

但是,这在返回类型中重载了addZCols,这是不允许的,并导致错误,导致呼叫addZCalls的过程很模糊,无法选择2个候选者之一。我想要的是,B+1的版本仅被称为基本情况,可以说是Z=1时的情况。

关于如何进行这项工作或采用其他方法的任何想法吗?

2 个答案:

答案 0 :(得分:2)

如果我正确理解了您的要求,则可以简单地编写如下功能模板:

mx-auto

然后像这样使用它:

template<int A, int B, int Z = 1>
Matrix<A,B+Z> addZCols(Matrix<A,B> m1) {
    return Matrix<A,B+Z>{};
}

默认情况下,第三个参数是Matrix<1,2> a = addZCols(Matrix<1,1>{}); Matrix<1,4> b = addZCols<1,1,3>(Matrix<1,1>{}); ,因此该功能模板可以用作1

@Evg指出,模板参数具有不错的属性,默认参数可以按任何顺序出现,因此我们可以将addOneCol参数放在第一个位置:

Z

这使您可以更方便地拨打电话,如下所示:

template<int Z = 1, int A, int B>
Matrix<A,B+Z> addZCols(Matrix<A,B> m1) {
    return Matrix<A,B+Z>{};
}

由于只需要指定Matrix<1,2> a = addZCols(Matrix<1,1>{}); Matrix<1,4> b = addZCols<3>(Matrix<1,1>{}); ,因为Z,并且可以从A参数推导出B

答案 1 :(得分:1)

也许有一种更有效的方法,但是使用您建议的递归解决方案,SFINAE可用于消除模板功能的两个版本的歧义。

#include <type_traits>

template <int A, int B>
struct Matrix {
    constexpr int rows() const { return A; }
    constexpr int cols() const { return B; }

    int data;
};

template<int Z, int A, int B, std::enable_if_t<Z == 0, int> = 0>
Matrix<A, B> addZCols(Matrix<A,B> m1) {
    return m1;
}

template<int Z, int A, int B, std::enable_if_t<Z != 0, int> = 0>
Matrix<A,B+Z> addZCols(Matrix<A,B> m1) {
    return addOneCol(addZCols<Z-1, A, B>(m1));
}

template<int A, int B>
Matrix<A,B+1> addOneCol(Matrix<A,B> m1) {
    return Matrix<A,B+1>();
}

int main() {
    Matrix<2, 2> m1;
    auto m2 = addZCols<3>(m1);

    static_assert(m2.rows() == 2, "check rows");
    static_assert(m2.cols() == 5, "check cols");

    return 0;
}

为了清楚起见,我也将递归限制偏移了一个,并对addZCols的模板参数进行了重新排序,以使其调用起来更好,但是它与原始签名的作用相同。