如何管理具有递归函数调用的模板类中的数组(以数组的长度作为参数)?

Posted

技术标签:

【中文标题】如何管理具有递归函数调用的模板类中的数组(以数组的长度作为参数)?【英文标题】:How do you manage an array in a template class (with the length of the array as a parameter) that has recursive function calls? 【发布时间】:2022-01-23 21:02:20 【问题描述】:

这是我的部分代码的要点:

#include <iostream>
using namespace std;

class Exception
 ;

template <int row_length>
class IntContainer

private:
    int mem[row_length];
public:
    IntContainer()
    
        for (int i = 0; i < row_length; i++) mem[i] = i;
    

    ~IntContainer() 

    int get_det()
    
        if (row_length == 2) return mem[0] + mem[1];
        else if (row_length == 1) return mem[0];
        else return get_cofactor();
    

    int get_cofactor()
    
        if (row_length == 1) throw Exception();
        IntContainer<row_length - 1> temp;
        return temp.get_det();
    
;


int main()

    IntContainer<5> ic1;
    cout << ic1.get_det();

用g++编译,出现如下错误:

error: size '18446744073709551615' of array exceeds maximum object size '9223372036854775807'
11|  int mem[row_length];

我应该怎么做才能在没有编译错误的情况下管理数组(我在函数中正确处理)?

【问题讨论】:

您需要为 row_length of 1 专门化模板,以确保它不会尝试递归创建数组大小为 0 或更小的实例 你如何专门化 row_length 参数? 【参考方案1】:

为什么会这样

即使row_length 为 1 或 2 时不会调用 get_cofactor(),它仍然会被定义。

这反过来又为row_length 定义了IntContainer,大小为1、0,最后是-1(环绕到MAX_INT)。

您可以在编译器资源管理器的 gcc 11.2 的 diagnostic output 中看到这一点:

<source>: In instantiation of 'class IntContainer<-1>':
<source>:30:38:   required from 'int IntContainer<row_length>::get_cofactor() [with int row_length = 0]'
<source>:24:21:   required from 'int IntContainer<row_length>::get_det() [with int row_length = 0]'
<source>:31:28:   required from 'int IntContainer<row_length>::get_cofactor() [with int row_length = 1]'
<source>:24:21:   required from 'int IntContainer<row_length>::get_det() [with int row_length = 1]'
<source>:31:28:   required from 'int IntContainer<row_length>::get_cofactor() [with int row_length = 2]'
<source>:24:21:   [ skipping 2 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
<source>:24:21:   required from 'int IntContainer<row_length>::get_det() [with int row_length = 3]'
<source>:31:28:   required from 'int IntContainer<row_length>::get_cofactor() [with int row_length = 4]'
<source>:24:21:   required from 'int IntContainer<row_length>::get_det() [with int row_length = 4]'
<source>:31:28:   required from 'int IntContainer<row_length>::get_cofactor() [with int row_length = 5]'
<source>:24:21:   required from 'int IntContainer<row_length>::get_det() [with int row_length = 5]'
<source>:39:24:   required from here
<source>:11:9: error: size '18446744073709551615' of array exceeds maximum object size '9223372036854775807'
   11 |     int mem[row_length];
      |         ^~~
Compiler returned: 1

C++17if constexpr解决方案

如果您使用的是 C++17,那么一种解决方案是将 get_det() 中的条件句转换为 if constexpr 语句

这会抑制对get_cofactor 的调用,应该...抑制它被定义。那时我对模板很模糊。如果这不是可移植的,它可能需要模板专业化。

#include <iostream>
using namespace std;

class Exception
 ;

template <int row_length>
class IntContainer

private:
    int mem[row_length];
public:
    IntContainer()
    
        for (int i = 0; i < row_length; i++) mem[i] = i;
    

    ~IntContainer() 

    int get_det()
    
        if constexpr (row_length == 2) return mem[0] + mem[1];
        else if constexpr (row_length == 1) return mem[0];
        else return get_cofactor();
    

    int get_cofactor()
    
        if constexpr (row_length == 1) 
            throw Exception();
            return 0;
        
        else 
            IntContainer<row_length - 1> temp;
            return temp.get_det();
        
    
;


int main()

    IntContainer<5> ic1;
    cout << ic1.get_det();

特殊递归退出案例的模板特化

FWIW 我认为重写它以使用模板专业化可能会更简洁:

#include <iostream>
using namespace std;

class Exception
 ;

template <int row_length>
class IntContainer

private:
    int mem[row_length];
public:
    IntContainer()
    
        for (int i = 0; i < row_length; i++) mem[i] = i;
    

    ~IntContainer() 

    int get_det()
    
        std::cout << "Entered get_det() row_length = " << row_length << '\n';
        return get_cofactor();
    

    int get_cofactor()
    
        std::cout << "Entered get_cofactor() row_length = " << row_length << '\n';
        IntContainer<row_length - 1> temp;
        return temp.get_det();
    
;

template <>
int IntContainer<1>::get_det() 
    std::cout << "Entered specialized get_det() row_length = " << 1 << '\n';
    return mem[0];


template <>
int IntContainer<1>::get_cofactor() 
    std::cout << "Entered specialized get_cofactor() row_length = " << 1 << '\n';
    // throw Exception();
    return 0;


template <>
int IntContainer<2>::get_det() 
    std::cout << "Entered specialized get_det() row_length = " << 2 << '\n';
    return mem[0] + mem[1];



int main()

    IntContainer<5> ic1;
    cout << ic1.get_det() << '\n';

    IntContainer<1>.get_cofactor();

以上输出

Entered get_det() row_length = 5
Entered get_cofactor() row_length = 5
Entered get_det() row_length = 4
Entered get_cofactor() row_length = 4
Entered get_det() row_length = 3
Entered get_cofactor() row_length = 3
Entered specialized get_det() row_length = 2
1
Entered specialized get_cofactor() row_length = 1

【讨论】:

可能值得注意的是,if constexpr 需要 C++17,有些人可能还不能使用。在这种情况下,模板专业化是解决问题的“旧方法” 您还应该在get_cofactor 中使用if constexpr,因为这是递归实际发生的地方。 template&lt;&gt; int IntContainer&lt;1&gt;::get_det() 有效吗?我认为您必须专门化整个封闭类模板,而不能只专门化成员函数的定义。 @NathanPierson 我运行它。不确定它是否完整。我可能会遗漏一些边缘情况。我知道您不能对课程进行部分模板专业化,但这可以按书面方式进行。如果你弄清楚了,请告诉我。

以上是关于如何管理具有递归函数调用的模板类中的数组(以数组的长度作为参数)?的主要内容,如果未能解决你的问题,请参考以下文章

Java中如何调用枚举类中的数组

使用选择算法编译时间递归排序

从C中的函数返回具有多个可变长度数组的结构

如何使用自定义值初始化模板类中的数组?

这个递归数组置换函数如何在幕后工作?

编写对具有n个元素的一维数组求和函数ArraySum()