C++数组大小推导模板解析及相关技术问题咨询
我在ArrayUnion2.cpp里实现了一个能自动推导方阵尺寸的C++模板函数CompareRMatrixResults,用来验证同尺寸方阵是否近似相等。这个模板第一个参数可接收2x2或4x4的方阵,第二个参数是4x4的creal_T类型矩阵(当N=2时会将其视为2x2矩阵处理)。但我对该模板的内部原理存在以下疑问:
- 参数
(&R)[N]中的&为何必需?是否有其他可行的函数签名? (&R)[N]这个语法到底是什么意思?- 既然处理的是方阵,为何无需使用
[N][N]的参数形式? - 模板参数
N是如何被自动推导出来的?
相关代码及运行结果
ArrayUnion2.cpp
#include <iostream> #include "Array_3rd_Party.hpp" struct ResultsXcorrMatrix // 用于处理2x2和4x4矩阵的结构体 { int rc{}; union { creal_T RMatrixData[16]{}; creal_T RMatrixData2[2][2]; creal_T RMatrixData4[4][4]; }; }; // 可正常工作,因为调用时传入的是RMatrixData2或RMatrixData4 template<typename T, int N> void CompareRMatrixResults(T (&R)[N], creal_T M[4][4]) { std::cout << "CompareRMatrixResults: Display R: N = " << N << std::endl; for (int rr = 0; rr < N; ++rr) { for (int cc = 0; cc < N; ++cc) { std::cout << R[cc][rr].re << " + " << R[cc][rr].im << "i\t"; } std::cout << "\n"; } std::cout << "\n"; std::cout << "CompareRMatrixResults: Display M: N = " << N << std::endl; for (int rr = 0; rr < N; ++rr) { for (int cc = 0; cc < N; ++cc) { std::cout << M[cc][rr].re << " + " << M[cc][rr].im << "i\t"; } std::cout << "\n"; } std::cout << "\n"; // 矩阵比较逻辑写在此处 } int main() { ResultsXcorrMatrix res{}; // ******** 打印2x2数组 ********** GenerateMatrix(res.RMatrixData, 2); // 生成2x2矩阵 INPUT_OUTPUT_DATA_TYPE* ioDataPtr = &inputOutputData[0]; INPUT_OUTPUT_DATA_TYPE::Outputs ioDataOutputs = ioDataPtr->outputs; std::cout << " *** CompareRMatrixResults(res.RMatrixData2, ioDataOutputs.R_Corr_Matrix); ***\n" << std::endl; CompareRMatrixResults(res.RMatrixData2, ioDataOutputs.R_Corr_Matrix); // ******** 打印4x4数组 ********** GenerateMatrix(res.RMatrixData, 4); // 生成4x4矩阵 ioDataPtr = &inputOutputData[1]; ioDataOutputs = ioDataPtr->outputs; std::cout << " *** CompareRMatrixResults(res.RMatrixData4, ioDataOutputs.R_Corr_Matrix); ***\n" << std::endl; CompareRMatrixResults(res.RMatrixData4, ioDataOutputs.R_Corr_Matrix); } // END main
Array_3rd_Party.hpp(注:第三方代码为列优先,索引已适配C++行优先)
#ifndef ARRAY_3RD_PARTY_HPP #define ARRAY_3RD_PARTY_HPP // 此头文件(及其中数据)来自第三方 // 注意:**** 第三方采用列优先存储 **** typedef struct { double re; double im; } creal_T; typedef struct Input_output_data_type { struct Outputs { int rc; creal_T R_Corr_Matrix[4][4]; // 也可用于表示2x2矩阵 } outputs; } INPUT_OUTPUT_DATA_TYPE; INPUT_OUTPUT_DATA_TYPE inputOutputData[2] = { { { // 2x2矩阵 0, { { {111.0, 111.1}, {222.0, 222.2} }, // 第1列 - 此行其余两个值为{0+0i} { {333.0, 333.3}, {444.0, 444.4} } // 第2列 } } }, { { // 4x4矩阵 0, { { {11.0, 11.1}, {12.0, 12.2}, {13.0, 13.3}, {14.0, 14.4} }, // 第1列 { {21.0, 21.1}, {22.0, 22.2}, {23.0, 23.3}, {24.0, 24.4} }, // 第2列 { {31.0, 31.1}, {32.0, 32.2}, {33.0, 33.3}, {34.0, 34.4} }, // 第3列 { {41.0, 41.1}, {42.0, 42.2}, {43.0, 43.3}, {44.0, 44.4} }, // 第4列 } // 此行其余两个值为{0+0i} } } }; // 输出填充好数据的2x2或4x4矩阵 void GenerateMatrix( creal_T R_matrix_data[], int N) { switch(N) { case 2: { R_matrix_data[0].re = 111.0; // 第1列 R_matrix_data[0].im = 111.1; R_matrix_data[1].re = 222.0; // 第1列 R_matrix_data[1].im = 222.2; R_matrix_data[2].re = 333.0; // 第2列 R_matrix_data[2].im = 333.3; R_matrix_data[3].re = 444.0; // 第2列 R_matrix_data[3].im = 444.4; } break; case 4: { R_matrix_data[0].re = 11.0; // 第1列 R_matrix_data[0].im = 11.1; R_matrix_data[1].re = 12.0; // 第1列 R_matrix_data[1].im = 12.2; R_matrix_data[2].re = 13.0; // 第1列 R_matrix_data[2].im = 13.3; R_matrix_data[3].re = 14.0; // 第1列 R_matrix_data[3].im = 14.4; R_matrix_data[0+4].re = 21.0; // 第2列 R_matrix_data[0+4].im = 21.1; R_matrix_data[1+4].re = 22.0; // 第2列 R_matrix_data[1+4].im = 22.2; R_matrix_data[2+4].re = 23.0; // 第2列 R_matrix_data[2+4].im = 23.3; R_matrix_data[3+4].re = 24.0; // 第2列 R_matrix_data[3+4].im = 24.4; R_matrix_data[0+8].re = 31.0; // 第3列 R_matrix_data[0+8].im = 31.1; R_matrix_data[1+8].re = 32.0; // 第3列 R_matrix_data[1+8].im = 32.2; R_matrix_data[2+8].re = 33.0; // 第3列 R_matrix_data[2+8].im = 33.3; R_matrix_data[3+8].re = 34.0; // 第3列 R_matrix_data[3+8].im = 34.4; R_matrix_data[0+12].re = 41.0; // 第4列 R_matrix_data[0+12].im = 41.1; R_matrix_data[1+12].re = 42.0; // 第4列 R_matrix_data[1+12].im = 42.2; R_matrix_data[2+12].re = 43.0; // 第4列 R_matrix_data[2+12].im = 43.3; R_matrix_data[3+12].re = 44.0; // 第4列 R_matrix_data[3+12].im = 44.4; } break; } } #endif
编译命令及运行结果
编译命令:
$ g++ --std=c++17 -Wall -g ArrayUnion2.cpp && ./a.exe
运行结果:
*** CompareRMatrixResults(res.RMatrixData2, ioDataOutputs.R_Corr_Matrix); *** CompareRMatrixResults: Display R: N = 2 111 + 111.1i 333 + 333.3i 222 + 222.2i 444 + 444.4i CompareRMatrixResults: Display M: N = 2 111 + 111.1i 333 + 333.3i 222 + 222.2i 444 + 444.4i *** CompareRMatrixResults(res.RMatrixData4, ioDataOutputs.R_Corr_Matrix); *** CompareRMatrixResults: Display R: N = 4 11 + 11.1i 21 + 21.1i 31 + 31.1i 41 + 41.1i 12 + 12.2i 22 + 22.2i 32 + 32.2i 42 + 42.2i 13 + 13.3i 23 + 23.3i 33 + 33.3i 43 + 43.3i 14 + 14.4i 24 + 24.4i 34 + 34.4i 44 + 44.4i CompareRMatrixResults: Display M: N = 4 11 + 11.1i 21 + 21.1i 31 + 31.1i 41 + 41.1i 12 + 12.2i 22 + 22.2i 32 + 32.2i 42 + 42.2i 13 + 13.3i 23 + 23.3i 33 + 33.3i 43 + 43.3i 14 + 14.4i 24 + 24.4i 34 + 34.4i 44 + 44.4i
问题解答
1. 参数(&R)[N]中的&为何必需?是否有其他可行的函数签名?
如果不加&,直接写T R[N],C++会把它等价于T* R——数组传参时会自动退化为指针,这样就丢失了数组的尺寸信息,模板参数N根本无法推导。加&是为了将参数声明为数组的引用,保留数组的完整类型(包括尺寸),编译器才能获取到N的值。
其他可行的签名比如使用std::array<T, N>& R,但针对原生数组,数组引用是最直接的方式。另外也可以手动指定N作为函数参数,但这样就失去了自动推导的便利性。
2. 如何解读(&R)[N]的语法含义?
这个语法可以拆分理解:[N]表示这是一个大小为N的数组,&R表示该数组以引用方式传递。合起来就是“R是一个大小为N的T类型数组的引用”。
注意优先级:如果写成T& R[N],会被解析为“R是一个存储T类型引用的数组”,这在C++中是非法的(引用不能存入数组)。所以必须加括号让&先绑定到R,再结合[N]表示数组引用。
3. 既然处理的是方阵,为何无需使用[N][N]的参数形式?
你传入的RMatrixData2是creal_T[2][2],RMatrixData4是creal_T[4][4],它们本质是“数组的数组”——比如creal_T[2][2]是一个大小为2的数组,每个元素又是creal_T[2]类型的数组。
当用T (&R)[N]接收时,T会被推导为creal_T[2](对应2x2矩阵)或creal_T[4](对应4x4矩阵),N就是外层数组的大小(即方阵的行数/列数)。函数里用R[cc][rr]就能访问元素,因为R的每个元素本身就是一个大小为N的数组,刚好匹配方阵结构。
当然也可以写成T (&R)[N][N],但当前写法通过模板自动推导内层数组类型,实现上更简洁,效果是等价的。
4. 模板参数N是如何被自动推导的?
C++模板类型推导规则中,当函数参数是数组的引用时,编译器会直接从传入的数组实参中提取数组的大小。比如传入res.RMatrixData2(类型为creal_T[2][2]),编译器匹配T (&R)[N]时,会把T推导为creal_T[2],N推导为2;传入res.RMatrixData4时,T推导为creal_T[4],N推导为4。
这个推导在编译阶段完成,编译器会检查实参的完整类型(因为是引用传递,没有数组退化),然后将对应的尺寸赋值给模板参数N。
内容的提问来源于stack exchange,提问作者PaulH

